Roo.ContentPanel - add @property view, which can hold the view element - eg. View...
[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
9211     this.el.on({
9212         "click": this.onClick,
9213         "dblclick": this.onDblClick,
9214         "contextmenu": this.onContextMenu,
9215         scope:this
9216     });
9217
9218     this.selections = [];
9219     this.nodes = [];
9220     this.cmp = new Roo.CompositeElementLite([]);
9221     if(this.store){
9222         this.store = Roo.factory(this.store, Roo.data);
9223         this.setStore(this.store, true);
9224     }
9225     Roo.View.superclass.constructor.call(this);
9226 };
9227
9228 Roo.extend(Roo.View, Roo.util.Observable, {
9229     
9230      /**
9231      * @cfg {Roo.data.Store} store Data store to load data from.
9232      */
9233     store : false,
9234     
9235     /**
9236      * @cfg {String|Roo.Element} el The container element.
9237      */
9238     el : '',
9239     
9240     /**
9241      * @cfg {String|Roo.Template} tpl The template used by this View 
9242      */
9243     tpl : false,
9244     
9245     /**
9246      * @cfg {String} selectedClass The css class to add to selected nodes
9247      */
9248     selectedClass : "x-view-selected",
9249      /**
9250      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9251      */
9252     emptyText : "",
9253     /**
9254      * @cfg {Boolean} multiSelect Allow multiple selection
9255      */
9256     
9257     multiSelect : false,
9258     /**
9259      * @cfg {Boolean} singleSelect Allow single selection
9260      */
9261     singleSelect:  false,
9262     
9263     /**
9264      * Returns the element this view is bound to.
9265      * @return {Roo.Element}
9266      */
9267     getEl : function(){
9268         return this.el;
9269     },
9270
9271     /**
9272      * Refreshes the view.
9273      */
9274     refresh : function(){
9275         var t = this.tpl;
9276         this.clearSelections();
9277         this.el.update("");
9278         var html = [];
9279         var records = this.store.getRange();
9280         if(records.length < 1){
9281             this.el.update(this.emptyText);
9282             return;
9283         }
9284         for(var i = 0, len = records.length; i < len; i++){
9285             var data = this.prepareData(records[i].data, i, records[i]);
9286             html[html.length] = t.apply(data);
9287         }
9288         this.el.update(html.join(""));
9289         this.nodes = this.el.dom.childNodes;
9290         this.updateIndexes(0);
9291     },
9292
9293     /**
9294      * Function to override to reformat the data that is sent to
9295      * the template for each node.
9296      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9297      * a JSON object for an UpdateManager bound view).
9298      */
9299     prepareData : function(data){
9300         return data;
9301     },
9302
9303     onUpdate : function(ds, record){
9304         this.clearSelections();
9305         var index = this.store.indexOf(record);
9306         var n = this.nodes[index];
9307         this.tpl.insertBefore(n, this.prepareData(record.data));
9308         n.parentNode.removeChild(n);
9309         this.updateIndexes(index, index);
9310     },
9311
9312     onAdd : function(ds, records, index){
9313         this.clearSelections();
9314         if(this.nodes.length == 0){
9315             this.refresh();
9316             return;
9317         }
9318         var n = this.nodes[index];
9319         for(var i = 0, len = records.length; i < len; i++){
9320             var d = this.prepareData(records[i].data);
9321             if(n){
9322                 this.tpl.insertBefore(n, d);
9323             }else{
9324                 this.tpl.append(this.el, d);
9325             }
9326         }
9327         this.updateIndexes(index);
9328     },
9329
9330     onRemove : function(ds, record, index){
9331         this.clearSelections();
9332         this.el.dom.removeChild(this.nodes[index]);
9333         this.updateIndexes(index);
9334     },
9335
9336     /**
9337      * Refresh an individual node.
9338      * @param {Number} index
9339      */
9340     refreshNode : function(index){
9341         this.onUpdate(this.store, this.store.getAt(index));
9342     },
9343
9344     updateIndexes : function(startIndex, endIndex){
9345         var ns = this.nodes;
9346         startIndex = startIndex || 0;
9347         endIndex = endIndex || ns.length - 1;
9348         for(var i = startIndex; i <= endIndex; i++){
9349             ns[i].nodeIndex = i;
9350         }
9351     },
9352
9353     /**
9354      * Changes the data store this view uses and refresh the view.
9355      * @param {Store} store
9356      */
9357     setStore : function(store, initial){
9358         if(!initial && this.store){
9359             this.store.un("datachanged", this.refresh);
9360             this.store.un("add", this.onAdd);
9361             this.store.un("remove", this.onRemove);
9362             this.store.un("update", this.onUpdate);
9363             this.store.un("clear", this.refresh);
9364         }
9365         if(store){
9366           
9367             store.on("datachanged", this.refresh, this);
9368             store.on("add", this.onAdd, this);
9369             store.on("remove", this.onRemove, this);
9370             store.on("update", this.onUpdate, this);
9371             store.on("clear", this.refresh, this);
9372         }
9373         
9374         if(store){
9375             this.refresh();
9376         }
9377     },
9378
9379     /**
9380      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9381      * @param {HTMLElement} node
9382      * @return {HTMLElement} The template node
9383      */
9384     findItemFromChild : function(node){
9385         var el = this.el.dom;
9386         if(!node || node.parentNode == el){
9387                     return node;
9388             }
9389             var p = node.parentNode;
9390             while(p && p != el){
9391             if(p.parentNode == el){
9392                 return p;
9393             }
9394             p = p.parentNode;
9395         }
9396             return null;
9397     },
9398
9399     /** @ignore */
9400     onClick : function(e){
9401         var item = this.findItemFromChild(e.getTarget());
9402         if(item){
9403             var index = this.indexOf(item);
9404             if(this.onItemClick(item, index, e) !== false){
9405                 this.fireEvent("click", this, index, item, e);
9406             }
9407         }else{
9408             this.clearSelections();
9409         }
9410     },
9411
9412     /** @ignore */
9413     onContextMenu : function(e){
9414         var item = this.findItemFromChild(e.getTarget());
9415         if(item){
9416             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9417         }
9418     },
9419
9420     /** @ignore */
9421     onDblClick : function(e){
9422         var item = this.findItemFromChild(e.getTarget());
9423         if(item){
9424             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9425         }
9426     },
9427
9428     onItemClick : function(item, index, e){
9429         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9430             return false;
9431         }
9432         if(this.multiSelect || this.singleSelect){
9433             if(this.multiSelect && e.shiftKey && this.lastSelection){
9434                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9435             }else{
9436                 this.select(item, this.multiSelect && e.ctrlKey);
9437                 this.lastSelection = item;
9438             }
9439             e.preventDefault();
9440         }
9441         return true;
9442     },
9443
9444     /**
9445      * Get the number of selected nodes.
9446      * @return {Number}
9447      */
9448     getSelectionCount : function(){
9449         return this.selections.length;
9450     },
9451
9452     /**
9453      * Get the currently selected nodes.
9454      * @return {Array} An array of HTMLElements
9455      */
9456     getSelectedNodes : function(){
9457         return this.selections;
9458     },
9459
9460     /**
9461      * Get the indexes of the selected nodes.
9462      * @return {Array}
9463      */
9464     getSelectedIndexes : function(){
9465         var indexes = [], s = this.selections;
9466         for(var i = 0, len = s.length; i < len; i++){
9467             indexes.push(s[i].nodeIndex);
9468         }
9469         return indexes;
9470     },
9471
9472     /**
9473      * Clear all selections
9474      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9475      */
9476     clearSelections : function(suppressEvent){
9477         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9478             this.cmp.elements = this.selections;
9479             this.cmp.removeClass(this.selectedClass);
9480             this.selections = [];
9481             if(!suppressEvent){
9482                 this.fireEvent("selectionchange", this, this.selections);
9483             }
9484         }
9485     },
9486
9487     /**
9488      * Returns true if the passed node is selected
9489      * @param {HTMLElement/Number} node The node or node index
9490      * @return {Boolean}
9491      */
9492     isSelected : function(node){
9493         var s = this.selections;
9494         if(s.length < 1){
9495             return false;
9496         }
9497         node = this.getNode(node);
9498         return s.indexOf(node) !== -1;
9499     },
9500
9501     /**
9502      * Selects nodes.
9503      * @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
9504      * @param {Boolean} keepExisting (optional) true to keep existing selections
9505      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9506      */
9507     select : function(nodeInfo, keepExisting, suppressEvent){
9508         if(nodeInfo instanceof Array){
9509             if(!keepExisting){
9510                 this.clearSelections(true);
9511             }
9512             for(var i = 0, len = nodeInfo.length; i < len; i++){
9513                 this.select(nodeInfo[i], true, true);
9514             }
9515         } else{
9516             var node = this.getNode(nodeInfo);
9517             if(node && !this.isSelected(node)){
9518                 if(!keepExisting){
9519                     this.clearSelections(true);
9520                 }
9521                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9522                     Roo.fly(node).addClass(this.selectedClass);
9523                     this.selections.push(node);
9524                     if(!suppressEvent){
9525                         this.fireEvent("selectionchange", this, this.selections);
9526                     }
9527                 }
9528             }
9529         }
9530     },
9531
9532     /**
9533      * Gets a template node.
9534      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9535      * @return {HTMLElement} The node or null if it wasn't found
9536      */
9537     getNode : function(nodeInfo){
9538         if(typeof nodeInfo == "string"){
9539             return document.getElementById(nodeInfo);
9540         }else if(typeof nodeInfo == "number"){
9541             return this.nodes[nodeInfo];
9542         }
9543         return nodeInfo;
9544     },
9545
9546     /**
9547      * Gets a range template nodes.
9548      * @param {Number} startIndex
9549      * @param {Number} endIndex
9550      * @return {Array} An array of nodes
9551      */
9552     getNodes : function(start, end){
9553         var ns = this.nodes;
9554         start = start || 0;
9555         end = typeof end == "undefined" ? ns.length - 1 : end;
9556         var nodes = [];
9557         if(start <= end){
9558             for(var i = start; i <= end; i++){
9559                 nodes.push(ns[i]);
9560             }
9561         } else{
9562             for(var i = start; i >= end; i--){
9563                 nodes.push(ns[i]);
9564             }
9565         }
9566         return nodes;
9567     },
9568
9569     /**
9570      * Finds the index of the passed node
9571      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9572      * @return {Number} The index of the node or -1
9573      */
9574     indexOf : function(node){
9575         node = this.getNode(node);
9576         if(typeof node.nodeIndex == "number"){
9577             return node.nodeIndex;
9578         }
9579         var ns = this.nodes;
9580         for(var i = 0, len = ns.length; i < len; i++){
9581             if(ns[i] == node){
9582                 return i;
9583             }
9584         }
9585         return -1;
9586     }
9587 });
9588 /*
9589  * Based on:
9590  * Ext JS Library 1.1.1
9591  * Copyright(c) 2006-2007, Ext JS, LLC.
9592  *
9593  * Originally Released Under LGPL - original licence link has changed is not relivant.
9594  *
9595  * Fork - LGPL
9596  * <script type="text/javascript">
9597  */
9598
9599 /**
9600  * @class Roo.JsonView
9601  * @extends Roo.View
9602  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9603 <pre><code>
9604 var view = new Roo.JsonView({
9605     container: "my-element",
9606     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9607     multiSelect: true, 
9608     jsonRoot: "data" 
9609 });
9610
9611 // listen for node click?
9612 view.on("click", function(vw, index, node, e){
9613     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9614 });
9615
9616 // direct load of JSON data
9617 view.load("foobar.php");
9618
9619 // Example from my blog list
9620 var tpl = new Roo.Template(
9621     '&lt;div class="entry"&gt;' +
9622     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9623     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9624     "&lt;/div&gt;&lt;hr /&gt;"
9625 );
9626
9627 var moreView = new Roo.JsonView({
9628     container :  "entry-list", 
9629     template : tpl,
9630     jsonRoot: "posts"
9631 });
9632 moreView.on("beforerender", this.sortEntries, this);
9633 moreView.load({
9634     url: "/blog/get-posts.php",
9635     params: "allposts=true",
9636     text: "Loading Blog Entries..."
9637 });
9638 </code></pre>
9639
9640 * Note: old code is supported with arguments : (container, template, config)
9641
9642
9643  * @constructor
9644  * Create a new JsonView
9645  * 
9646  * @param {Object} config The config object
9647  * 
9648  */
9649 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9650     
9651     
9652     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9653
9654     var um = this.el.getUpdateManager();
9655     um.setRenderer(this);
9656     um.on("update", this.onLoad, this);
9657     um.on("failure", this.onLoadException, this);
9658
9659     /**
9660      * @event beforerender
9661      * Fires before rendering of the downloaded JSON data.
9662      * @param {Roo.JsonView} this
9663      * @param {Object} data The JSON data loaded
9664      */
9665     /**
9666      * @event load
9667      * Fires when data is loaded.
9668      * @param {Roo.JsonView} this
9669      * @param {Object} data The JSON data loaded
9670      * @param {Object} response The raw Connect response object
9671      */
9672     /**
9673      * @event loadexception
9674      * Fires when loading fails.
9675      * @param {Roo.JsonView} this
9676      * @param {Object} response The raw Connect response object
9677      */
9678     this.addEvents({
9679         'beforerender' : true,
9680         'load' : true,
9681         'loadexception' : true
9682     });
9683 };
9684 Roo.extend(Roo.JsonView, Roo.View, {
9685     /**
9686      * @type {String} The root property in the loaded JSON object that contains the data
9687      */
9688     jsonRoot : "",
9689
9690     /**
9691      * Refreshes the view.
9692      */
9693     refresh : function(){
9694         this.clearSelections();
9695         this.el.update("");
9696         var html = [];
9697         var o = this.jsonData;
9698         if(o && o.length > 0){
9699             for(var i = 0, len = o.length; i < len; i++){
9700                 var data = this.prepareData(o[i], i, o);
9701                 html[html.length] = this.tpl.apply(data);
9702             }
9703         }else{
9704             html.push(this.emptyText);
9705         }
9706         this.el.update(html.join(""));
9707         this.nodes = this.el.dom.childNodes;
9708         this.updateIndexes(0);
9709     },
9710
9711     /**
9712      * 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.
9713      * @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:
9714      <pre><code>
9715      view.load({
9716          url: "your-url.php",
9717          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9718          callback: yourFunction,
9719          scope: yourObject, //(optional scope)
9720          discardUrl: false,
9721          nocache: false,
9722          text: "Loading...",
9723          timeout: 30,
9724          scripts: false
9725      });
9726      </code></pre>
9727      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9728      * 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.
9729      * @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}
9730      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9731      * @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.
9732      */
9733     load : function(){
9734         var um = this.el.getUpdateManager();
9735         um.update.apply(um, arguments);
9736     },
9737
9738     render : function(el, response){
9739         this.clearSelections();
9740         this.el.update("");
9741         var o;
9742         try{
9743             o = Roo.util.JSON.decode(response.responseText);
9744             if(this.jsonRoot){
9745                 
9746                 o = o[this.jsonRoot];
9747             }
9748         } catch(e){
9749         }
9750         /**
9751          * The current JSON data or null
9752          */
9753         this.jsonData = o;
9754         this.beforeRender();
9755         this.refresh();
9756     },
9757
9758 /**
9759  * Get the number of records in the current JSON dataset
9760  * @return {Number}
9761  */
9762     getCount : function(){
9763         return this.jsonData ? this.jsonData.length : 0;
9764     },
9765
9766 /**
9767  * Returns the JSON object for the specified node(s)
9768  * @param {HTMLElement/Array} node The node or an array of nodes
9769  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9770  * you get the JSON object for the node
9771  */
9772     getNodeData : function(node){
9773         if(node instanceof Array){
9774             var data = [];
9775             for(var i = 0, len = node.length; i < len; i++){
9776                 data.push(this.getNodeData(node[i]));
9777             }
9778             return data;
9779         }
9780         return this.jsonData[this.indexOf(node)] || null;
9781     },
9782
9783     beforeRender : function(){
9784         this.snapshot = this.jsonData;
9785         if(this.sortInfo){
9786             this.sort.apply(this, this.sortInfo);
9787         }
9788         this.fireEvent("beforerender", this, this.jsonData);
9789     },
9790
9791     onLoad : function(el, o){
9792         this.fireEvent("load", this, this.jsonData, o);
9793     },
9794
9795     onLoadException : function(el, o){
9796         this.fireEvent("loadexception", this, o);
9797     },
9798
9799 /**
9800  * Filter the data by a specific property.
9801  * @param {String} property A property on your JSON objects
9802  * @param {String/RegExp} value Either string that the property values
9803  * should start with, or a RegExp to test against the property
9804  */
9805     filter : function(property, value){
9806         if(this.jsonData){
9807             var data = [];
9808             var ss = this.snapshot;
9809             if(typeof value == "string"){
9810                 var vlen = value.length;
9811                 if(vlen == 0){
9812                     this.clearFilter();
9813                     return;
9814                 }
9815                 value = value.toLowerCase();
9816                 for(var i = 0, len = ss.length; i < len; i++){
9817                     var o = ss[i];
9818                     if(o[property].substr(0, vlen).toLowerCase() == value){
9819                         data.push(o);
9820                     }
9821                 }
9822             } else if(value.exec){ // regex?
9823                 for(var i = 0, len = ss.length; i < len; i++){
9824                     var o = ss[i];
9825                     if(value.test(o[property])){
9826                         data.push(o);
9827                     }
9828                 }
9829             } else{
9830                 return;
9831             }
9832             this.jsonData = data;
9833             this.refresh();
9834         }
9835     },
9836
9837 /**
9838  * Filter by a function. The passed function will be called with each
9839  * object in the current dataset. If the function returns true the value is kept,
9840  * otherwise it is filtered.
9841  * @param {Function} fn
9842  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9843  */
9844     filterBy : function(fn, scope){
9845         if(this.jsonData){
9846             var data = [];
9847             var ss = this.snapshot;
9848             for(var i = 0, len = ss.length; i < len; i++){
9849                 var o = ss[i];
9850                 if(fn.call(scope || this, o)){
9851                     data.push(o);
9852                 }
9853             }
9854             this.jsonData = data;
9855             this.refresh();
9856         }
9857     },
9858
9859 /**
9860  * Clears the current filter.
9861  */
9862     clearFilter : function(){
9863         if(this.snapshot && this.jsonData != this.snapshot){
9864             this.jsonData = this.snapshot;
9865             this.refresh();
9866         }
9867     },
9868
9869
9870 /**
9871  * Sorts the data for this view and refreshes it.
9872  * @param {String} property A property on your JSON objects to sort on
9873  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9874  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9875  */
9876     sort : function(property, dir, sortType){
9877         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9878         if(this.jsonData){
9879             var p = property;
9880             var dsc = dir && dir.toLowerCase() == "desc";
9881             var f = function(o1, o2){
9882                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9883                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9884                 ;
9885                 if(v1 < v2){
9886                     return dsc ? +1 : -1;
9887                 } else if(v1 > v2){
9888                     return dsc ? -1 : +1;
9889                 } else{
9890                     return 0;
9891                 }
9892             };
9893             this.jsonData.sort(f);
9894             this.refresh();
9895             if(this.jsonData != this.snapshot){
9896                 this.snapshot.sort(f);
9897             }
9898         }
9899     }
9900 });/*
9901  * Based on:
9902  * Ext JS Library 1.1.1
9903  * Copyright(c) 2006-2007, Ext JS, LLC.
9904  *
9905  * Originally Released Under LGPL - original licence link has changed is not relivant.
9906  *
9907  * Fork - LGPL
9908  * <script type="text/javascript">
9909  */
9910  
9911
9912 /**
9913  * @class Roo.ColorPalette
9914  * @extends Roo.Component
9915  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9916  * Here's an example of typical usage:
9917  * <pre><code>
9918 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9919 cp.render('my-div');
9920
9921 cp.on('select', function(palette, selColor){
9922     // do something with selColor
9923 });
9924 </code></pre>
9925  * @constructor
9926  * Create a new ColorPalette
9927  * @param {Object} config The config object
9928  */
9929 Roo.ColorPalette = function(config){
9930     Roo.ColorPalette.superclass.constructor.call(this, config);
9931     this.addEvents({
9932         /**
9933              * @event select
9934              * Fires when a color is selected
9935              * @param {ColorPalette} this
9936              * @param {String} color The 6-digit color hex code (without the # symbol)
9937              */
9938         select: true
9939     });
9940
9941     if(this.handler){
9942         this.on("select", this.handler, this.scope, true);
9943     }
9944 };
9945 Roo.extend(Roo.ColorPalette, Roo.Component, {
9946     /**
9947      * @cfg {String} itemCls
9948      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9949      */
9950     itemCls : "x-color-palette",
9951     /**
9952      * @cfg {String} value
9953      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9954      * the hex codes are case-sensitive.
9955      */
9956     value : null,
9957     clickEvent:'click',
9958     // private
9959     ctype: "Roo.ColorPalette",
9960
9961     /**
9962      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9963      */
9964     allowReselect : false,
9965
9966     /**
9967      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9968      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9969      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9970      * of colors with the width setting until the box is symmetrical.</p>
9971      * <p>You can override individual colors if needed:</p>
9972      * <pre><code>
9973 var cp = new Roo.ColorPalette();
9974 cp.colors[0] = "FF0000";  // change the first box to red
9975 </code></pre>
9976
9977 Or you can provide a custom array of your own for complete control:
9978 <pre><code>
9979 var cp = new Roo.ColorPalette();
9980 cp.colors = ["000000", "993300", "333300"];
9981 </code></pre>
9982      * @type Array
9983      */
9984     colors : [
9985         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9986         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9987         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9988         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9989         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9990     ],
9991
9992     // private
9993     onRender : function(container, position){
9994         var t = new Roo.MasterTemplate(
9995             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9996         );
9997         var c = this.colors;
9998         for(var i = 0, len = c.length; i < len; i++){
9999             t.add([c[i]]);
10000         }
10001         var el = document.createElement("div");
10002         el.className = this.itemCls;
10003         t.overwrite(el);
10004         container.dom.insertBefore(el, position);
10005         this.el = Roo.get(el);
10006         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10007         if(this.clickEvent != 'click'){
10008             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10009         }
10010     },
10011
10012     // private
10013     afterRender : function(){
10014         Roo.ColorPalette.superclass.afterRender.call(this);
10015         if(this.value){
10016             var s = this.value;
10017             this.value = null;
10018             this.select(s);
10019         }
10020     },
10021
10022     // private
10023     handleClick : function(e, t){
10024         e.preventDefault();
10025         if(!this.disabled){
10026             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10027             this.select(c.toUpperCase());
10028         }
10029     },
10030
10031     /**
10032      * Selects the specified color in the palette (fires the select event)
10033      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10034      */
10035     select : function(color){
10036         color = color.replace("#", "");
10037         if(color != this.value || this.allowReselect){
10038             var el = this.el;
10039             if(this.value){
10040                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10041             }
10042             el.child("a.color-"+color).addClass("x-color-palette-sel");
10043             this.value = color;
10044             this.fireEvent("select", this, color);
10045         }
10046     }
10047 });/*
10048  * Based on:
10049  * Ext JS Library 1.1.1
10050  * Copyright(c) 2006-2007, Ext JS, LLC.
10051  *
10052  * Originally Released Under LGPL - original licence link has changed is not relivant.
10053  *
10054  * Fork - LGPL
10055  * <script type="text/javascript">
10056  */
10057  
10058 /**
10059  * @class Roo.DatePicker
10060  * @extends Roo.Component
10061  * Simple date picker class.
10062  * @constructor
10063  * Create a new DatePicker
10064  * @param {Object} config The config object
10065  */
10066 Roo.DatePicker = function(config){
10067     Roo.DatePicker.superclass.constructor.call(this, config);
10068
10069     this.value = config && config.value ?
10070                  config.value.clearTime() : new Date().clearTime();
10071
10072     this.addEvents({
10073         /**
10074              * @event select
10075              * Fires when a date is selected
10076              * @param {DatePicker} this
10077              * @param {Date} date The selected date
10078              */
10079         select: true
10080     });
10081
10082     if(this.handler){
10083         this.on("select", this.handler,  this.scope || this);
10084     }
10085     // build the disabledDatesRE
10086     if(!this.disabledDatesRE && this.disabledDates){
10087         var dd = this.disabledDates;
10088         var re = "(?:";
10089         for(var i = 0; i < dd.length; i++){
10090             re += dd[i];
10091             if(i != dd.length-1) re += "|";
10092         }
10093         this.disabledDatesRE = new RegExp(re + ")");
10094     }
10095 };
10096
10097 Roo.extend(Roo.DatePicker, Roo.Component, {
10098     /**
10099      * @cfg {String} todayText
10100      * The text to display on the button that selects the current date (defaults to "Today")
10101      */
10102     todayText : "Today",
10103     /**
10104      * @cfg {String} okText
10105      * The text to display on the ok button
10106      */
10107     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10108     /**
10109      * @cfg {String} cancelText
10110      * The text to display on the cancel button
10111      */
10112     cancelText : "Cancel",
10113     /**
10114      * @cfg {String} todayTip
10115      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10116      */
10117     todayTip : "{0} (Spacebar)",
10118     /**
10119      * @cfg {Date} minDate
10120      * Minimum allowable date (JavaScript date object, defaults to null)
10121      */
10122     minDate : null,
10123     /**
10124      * @cfg {Date} maxDate
10125      * Maximum allowable date (JavaScript date object, defaults to null)
10126      */
10127     maxDate : null,
10128     /**
10129      * @cfg {String} minText
10130      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10131      */
10132     minText : "This date is before the minimum date",
10133     /**
10134      * @cfg {String} maxText
10135      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10136      */
10137     maxText : "This date is after the maximum date",
10138     /**
10139      * @cfg {String} format
10140      * The default date format string which can be overriden for localization support.  The format must be
10141      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10142      */
10143     format : "m/d/y",
10144     /**
10145      * @cfg {Array} disabledDays
10146      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10147      */
10148     disabledDays : null,
10149     /**
10150      * @cfg {String} disabledDaysText
10151      * The tooltip to display when the date falls on a disabled day (defaults to "")
10152      */
10153     disabledDaysText : "",
10154     /**
10155      * @cfg {RegExp} disabledDatesRE
10156      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10157      */
10158     disabledDatesRE : null,
10159     /**
10160      * @cfg {String} disabledDatesText
10161      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10162      */
10163     disabledDatesText : "",
10164     /**
10165      * @cfg {Boolean} constrainToViewport
10166      * True to constrain the date picker to the viewport (defaults to true)
10167      */
10168     constrainToViewport : true,
10169     /**
10170      * @cfg {Array} monthNames
10171      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10172      */
10173     monthNames : Date.monthNames,
10174     /**
10175      * @cfg {Array} dayNames
10176      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10177      */
10178     dayNames : Date.dayNames,
10179     /**
10180      * @cfg {String} nextText
10181      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10182      */
10183     nextText: 'Next Month (Control+Right)',
10184     /**
10185      * @cfg {String} prevText
10186      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10187      */
10188     prevText: 'Previous Month (Control+Left)',
10189     /**
10190      * @cfg {String} monthYearText
10191      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10192      */
10193     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10194     /**
10195      * @cfg {Number} startDay
10196      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10197      */
10198     startDay : 0,
10199     /**
10200      * @cfg {Bool} showClear
10201      * Show a clear button (usefull for date form elements that can be blank.)
10202      */
10203     
10204     showClear: false,
10205     
10206     /**
10207      * Sets the value of the date field
10208      * @param {Date} value The date to set
10209      */
10210     setValue : function(value){
10211         var old = this.value;
10212         this.value = value.clearTime(true);
10213         if(this.el){
10214             this.update(this.value);
10215         }
10216     },
10217
10218     /**
10219      * Gets the current selected value of the date field
10220      * @return {Date} The selected date
10221      */
10222     getValue : function(){
10223         return this.value;
10224     },
10225
10226     // private
10227     focus : function(){
10228         if(this.el){
10229             this.update(this.activeDate);
10230         }
10231     },
10232
10233     // private
10234     onRender : function(container, position){
10235         var m = [
10236              '<table cellspacing="0">',
10237                 '<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>',
10238                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10239         var dn = this.dayNames;
10240         for(var i = 0; i < 7; i++){
10241             var d = this.startDay+i;
10242             if(d > 6){
10243                 d = d-7;
10244             }
10245             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10246         }
10247         m[m.length] = "</tr></thead><tbody><tr>";
10248         for(var i = 0; i < 42; i++) {
10249             if(i % 7 == 0 && i != 0){
10250                 m[m.length] = "</tr><tr>";
10251             }
10252             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10253         }
10254         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10255             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10256
10257         var el = document.createElement("div");
10258         el.className = "x-date-picker";
10259         el.innerHTML = m.join("");
10260
10261         container.dom.insertBefore(el, position);
10262
10263         this.el = Roo.get(el);
10264         this.eventEl = Roo.get(el.firstChild);
10265
10266         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10267             handler: this.showPrevMonth,
10268             scope: this,
10269             preventDefault:true,
10270             stopDefault:true
10271         });
10272
10273         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10274             handler: this.showNextMonth,
10275             scope: this,
10276             preventDefault:true,
10277             stopDefault:true
10278         });
10279
10280         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10281
10282         this.monthPicker = this.el.down('div.x-date-mp');
10283         this.monthPicker.enableDisplayMode('block');
10284         
10285         var kn = new Roo.KeyNav(this.eventEl, {
10286             "left" : function(e){
10287                 e.ctrlKey ?
10288                     this.showPrevMonth() :
10289                     this.update(this.activeDate.add("d", -1));
10290             },
10291
10292             "right" : function(e){
10293                 e.ctrlKey ?
10294                     this.showNextMonth() :
10295                     this.update(this.activeDate.add("d", 1));
10296             },
10297
10298             "up" : function(e){
10299                 e.ctrlKey ?
10300                     this.showNextYear() :
10301                     this.update(this.activeDate.add("d", -7));
10302             },
10303
10304             "down" : function(e){
10305                 e.ctrlKey ?
10306                     this.showPrevYear() :
10307                     this.update(this.activeDate.add("d", 7));
10308             },
10309
10310             "pageUp" : function(e){
10311                 this.showNextMonth();
10312             },
10313
10314             "pageDown" : function(e){
10315                 this.showPrevMonth();
10316             },
10317
10318             "enter" : function(e){
10319                 e.stopPropagation();
10320                 return true;
10321             },
10322
10323             scope : this
10324         });
10325
10326         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10327
10328         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10329
10330         this.el.unselectable();
10331         
10332         this.cells = this.el.select("table.x-date-inner tbody td");
10333         this.textNodes = this.el.query("table.x-date-inner tbody span");
10334
10335         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10336             text: "&#160;",
10337             tooltip: this.monthYearText
10338         });
10339
10340         this.mbtn.on('click', this.showMonthPicker, this);
10341         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10342
10343
10344         var today = (new Date()).dateFormat(this.format);
10345         
10346         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10347         if (this.showClear) {
10348             baseTb.add( new Roo.Toolbar.Fill());
10349         }
10350         baseTb.add({
10351             text: String.format(this.todayText, today),
10352             tooltip: String.format(this.todayTip, today),
10353             handler: this.selectToday,
10354             scope: this
10355         });
10356         
10357         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10358             
10359         //});
10360         if (this.showClear) {
10361             
10362             baseTb.add( new Roo.Toolbar.Fill());
10363             baseTb.add({
10364                 text: '&#160;',
10365                 cls: 'x-btn-icon x-btn-clear',
10366                 handler: function() {
10367                     //this.value = '';
10368                     this.fireEvent("select", this, '');
10369                 },
10370                 scope: this
10371             });
10372         }
10373         
10374         
10375         if(Roo.isIE){
10376             this.el.repaint();
10377         }
10378         this.update(this.value);
10379     },
10380
10381     createMonthPicker : function(){
10382         if(!this.monthPicker.dom.firstChild){
10383             var buf = ['<table border="0" cellspacing="0">'];
10384             for(var i = 0; i < 6; i++){
10385                 buf.push(
10386                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10387                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10388                     i == 0 ?
10389                     '<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>' :
10390                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10391                 );
10392             }
10393             buf.push(
10394                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10395                     this.okText,
10396                     '</button><button type="button" class="x-date-mp-cancel">',
10397                     this.cancelText,
10398                     '</button></td></tr>',
10399                 '</table>'
10400             );
10401             this.monthPicker.update(buf.join(''));
10402             this.monthPicker.on('click', this.onMonthClick, this);
10403             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10404
10405             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10406             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10407
10408             this.mpMonths.each(function(m, a, i){
10409                 i += 1;
10410                 if((i%2) == 0){
10411                     m.dom.xmonth = 5 + Math.round(i * .5);
10412                 }else{
10413                     m.dom.xmonth = Math.round((i-1) * .5);
10414                 }
10415             });
10416         }
10417     },
10418
10419     showMonthPicker : function(){
10420         this.createMonthPicker();
10421         var size = this.el.getSize();
10422         this.monthPicker.setSize(size);
10423         this.monthPicker.child('table').setSize(size);
10424
10425         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10426         this.updateMPMonth(this.mpSelMonth);
10427         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10428         this.updateMPYear(this.mpSelYear);
10429
10430         this.monthPicker.slideIn('t', {duration:.2});
10431     },
10432
10433     updateMPYear : function(y){
10434         this.mpyear = y;
10435         var ys = this.mpYears.elements;
10436         for(var i = 1; i <= 10; i++){
10437             var td = ys[i-1], y2;
10438             if((i%2) == 0){
10439                 y2 = y + Math.round(i * .5);
10440                 td.firstChild.innerHTML = y2;
10441                 td.xyear = y2;
10442             }else{
10443                 y2 = y - (5-Math.round(i * .5));
10444                 td.firstChild.innerHTML = y2;
10445                 td.xyear = y2;
10446             }
10447             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10448         }
10449     },
10450
10451     updateMPMonth : function(sm){
10452         this.mpMonths.each(function(m, a, i){
10453             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10454         });
10455     },
10456
10457     selectMPMonth: function(m){
10458         
10459     },
10460
10461     onMonthClick : function(e, t){
10462         e.stopEvent();
10463         var el = new Roo.Element(t), pn;
10464         if(el.is('button.x-date-mp-cancel')){
10465             this.hideMonthPicker();
10466         }
10467         else if(el.is('button.x-date-mp-ok')){
10468             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10469             this.hideMonthPicker();
10470         }
10471         else if(pn = el.up('td.x-date-mp-month', 2)){
10472             this.mpMonths.removeClass('x-date-mp-sel');
10473             pn.addClass('x-date-mp-sel');
10474             this.mpSelMonth = pn.dom.xmonth;
10475         }
10476         else if(pn = el.up('td.x-date-mp-year', 2)){
10477             this.mpYears.removeClass('x-date-mp-sel');
10478             pn.addClass('x-date-mp-sel');
10479             this.mpSelYear = pn.dom.xyear;
10480         }
10481         else if(el.is('a.x-date-mp-prev')){
10482             this.updateMPYear(this.mpyear-10);
10483         }
10484         else if(el.is('a.x-date-mp-next')){
10485             this.updateMPYear(this.mpyear+10);
10486         }
10487     },
10488
10489     onMonthDblClick : function(e, t){
10490         e.stopEvent();
10491         var el = new Roo.Element(t), pn;
10492         if(pn = el.up('td.x-date-mp-month', 2)){
10493             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10494             this.hideMonthPicker();
10495         }
10496         else if(pn = el.up('td.x-date-mp-year', 2)){
10497             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10498             this.hideMonthPicker();
10499         }
10500     },
10501
10502     hideMonthPicker : function(disableAnim){
10503         if(this.monthPicker){
10504             if(disableAnim === true){
10505                 this.monthPicker.hide();
10506             }else{
10507                 this.monthPicker.slideOut('t', {duration:.2});
10508             }
10509         }
10510     },
10511
10512     // private
10513     showPrevMonth : function(e){
10514         this.update(this.activeDate.add("mo", -1));
10515     },
10516
10517     // private
10518     showNextMonth : function(e){
10519         this.update(this.activeDate.add("mo", 1));
10520     },
10521
10522     // private
10523     showPrevYear : function(){
10524         this.update(this.activeDate.add("y", -1));
10525     },
10526
10527     // private
10528     showNextYear : function(){
10529         this.update(this.activeDate.add("y", 1));
10530     },
10531
10532     // private
10533     handleMouseWheel : function(e){
10534         var delta = e.getWheelDelta();
10535         if(delta > 0){
10536             this.showPrevMonth();
10537             e.stopEvent();
10538         } else if(delta < 0){
10539             this.showNextMonth();
10540             e.stopEvent();
10541         }
10542     },
10543
10544     // private
10545     handleDateClick : function(e, t){
10546         e.stopEvent();
10547         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10548             this.setValue(new Date(t.dateValue));
10549             this.fireEvent("select", this, this.value);
10550         }
10551     },
10552
10553     // private
10554     selectToday : function(){
10555         this.setValue(new Date().clearTime());
10556         this.fireEvent("select", this, this.value);
10557     },
10558
10559     // private
10560     update : function(date){
10561         var vd = this.activeDate;
10562         this.activeDate = date;
10563         if(vd && this.el){
10564             var t = date.getTime();
10565             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10566                 this.cells.removeClass("x-date-selected");
10567                 this.cells.each(function(c){
10568                    if(c.dom.firstChild.dateValue == t){
10569                        c.addClass("x-date-selected");
10570                        setTimeout(function(){
10571                             try{c.dom.firstChild.focus();}catch(e){}
10572                        }, 50);
10573                        return false;
10574                    }
10575                 });
10576                 return;
10577             }
10578         }
10579         var days = date.getDaysInMonth();
10580         var firstOfMonth = date.getFirstDateOfMonth();
10581         var startingPos = firstOfMonth.getDay()-this.startDay;
10582
10583         if(startingPos <= this.startDay){
10584             startingPos += 7;
10585         }
10586
10587         var pm = date.add("mo", -1);
10588         var prevStart = pm.getDaysInMonth()-startingPos;
10589
10590         var cells = this.cells.elements;
10591         var textEls = this.textNodes;
10592         days += startingPos;
10593
10594         // convert everything to numbers so it's fast
10595         var day = 86400000;
10596         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10597         var today = new Date().clearTime().getTime();
10598         var sel = date.clearTime().getTime();
10599         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10600         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10601         var ddMatch = this.disabledDatesRE;
10602         var ddText = this.disabledDatesText;
10603         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10604         var ddaysText = this.disabledDaysText;
10605         var format = this.format;
10606
10607         var setCellClass = function(cal, cell){
10608             cell.title = "";
10609             var t = d.getTime();
10610             cell.firstChild.dateValue = t;
10611             if(t == today){
10612                 cell.className += " x-date-today";
10613                 cell.title = cal.todayText;
10614             }
10615             if(t == sel){
10616                 cell.className += " x-date-selected";
10617                 setTimeout(function(){
10618                     try{cell.firstChild.focus();}catch(e){}
10619                 }, 50);
10620             }
10621             // disabling
10622             if(t < min) {
10623                 cell.className = " x-date-disabled";
10624                 cell.title = cal.minText;
10625                 return;
10626             }
10627             if(t > max) {
10628                 cell.className = " x-date-disabled";
10629                 cell.title = cal.maxText;
10630                 return;
10631             }
10632             if(ddays){
10633                 if(ddays.indexOf(d.getDay()) != -1){
10634                     cell.title = ddaysText;
10635                     cell.className = " x-date-disabled";
10636                 }
10637             }
10638             if(ddMatch && format){
10639                 var fvalue = d.dateFormat(format);
10640                 if(ddMatch.test(fvalue)){
10641                     cell.title = ddText.replace("%0", fvalue);
10642                     cell.className = " x-date-disabled";
10643                 }
10644             }
10645         };
10646
10647         var i = 0;
10648         for(; i < startingPos; i++) {
10649             textEls[i].innerHTML = (++prevStart);
10650             d.setDate(d.getDate()+1);
10651             cells[i].className = "x-date-prevday";
10652             setCellClass(this, cells[i]);
10653         }
10654         for(; i < days; i++){
10655             intDay = i - startingPos + 1;
10656             textEls[i].innerHTML = (intDay);
10657             d.setDate(d.getDate()+1);
10658             cells[i].className = "x-date-active";
10659             setCellClass(this, cells[i]);
10660         }
10661         var extraDays = 0;
10662         for(; i < 42; i++) {
10663              textEls[i].innerHTML = (++extraDays);
10664              d.setDate(d.getDate()+1);
10665              cells[i].className = "x-date-nextday";
10666              setCellClass(this, cells[i]);
10667         }
10668
10669         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10670
10671         if(!this.internalRender){
10672             var main = this.el.dom.firstChild;
10673             var w = main.offsetWidth;
10674             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10675             Roo.fly(main).setWidth(w);
10676             this.internalRender = true;
10677             // opera does not respect the auto grow header center column
10678             // then, after it gets a width opera refuses to recalculate
10679             // without a second pass
10680             if(Roo.isOpera && !this.secondPass){
10681                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10682                 this.secondPass = true;
10683                 this.update.defer(10, this, [date]);
10684             }
10685         }
10686     }
10687 });        /*
10688  * Based on:
10689  * Ext JS Library 1.1.1
10690  * Copyright(c) 2006-2007, Ext JS, LLC.
10691  *
10692  * Originally Released Under LGPL - original licence link has changed is not relivant.
10693  *
10694  * Fork - LGPL
10695  * <script type="text/javascript">
10696  */
10697 /**
10698  * @class Roo.TabPanel
10699  * @extends Roo.util.Observable
10700  * A lightweight tab container.
10701  * <br><br>
10702  * Usage:
10703  * <pre><code>
10704 // basic tabs 1, built from existing content
10705 var tabs = new Roo.TabPanel("tabs1");
10706 tabs.addTab("script", "View Script");
10707 tabs.addTab("markup", "View Markup");
10708 tabs.activate("script");
10709
10710 // more advanced tabs, built from javascript
10711 var jtabs = new Roo.TabPanel("jtabs");
10712 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10713
10714 // set up the UpdateManager
10715 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10716 var updater = tab2.getUpdateManager();
10717 updater.setDefaultUrl("ajax1.htm");
10718 tab2.on('activate', updater.refresh, updater, true);
10719
10720 // Use setUrl for Ajax loading
10721 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10722 tab3.setUrl("ajax2.htm", null, true);
10723
10724 // Disabled tab
10725 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10726 tab4.disable();
10727
10728 jtabs.activate("jtabs-1");
10729  * </code></pre>
10730  * @constructor
10731  * Create a new TabPanel.
10732  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10733  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10734  */
10735 Roo.TabPanel = function(container, config){
10736     /**
10737     * The container element for this TabPanel.
10738     * @type Roo.Element
10739     */
10740     this.el = Roo.get(container, true);
10741     if(config){
10742         if(typeof config == "boolean"){
10743             this.tabPosition = config ? "bottom" : "top";
10744         }else{
10745             Roo.apply(this, config);
10746         }
10747     }
10748     if(this.tabPosition == "bottom"){
10749         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10750         this.el.addClass("x-tabs-bottom");
10751     }
10752     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10753     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10754     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10755     if(Roo.isIE){
10756         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10757     }
10758     if(this.tabPosition != "bottom"){
10759         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10760          * @type Roo.Element
10761          */
10762         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10763         this.el.addClass("x-tabs-top");
10764     }
10765     this.items = [];
10766
10767     this.bodyEl.setStyle("position", "relative");
10768
10769     this.active = null;
10770     this.activateDelegate = this.activate.createDelegate(this);
10771
10772     this.addEvents({
10773         /**
10774          * @event tabchange
10775          * Fires when the active tab changes
10776          * @param {Roo.TabPanel} this
10777          * @param {Roo.TabPanelItem} activePanel The new active tab
10778          */
10779         "tabchange": true,
10780         /**
10781          * @event beforetabchange
10782          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10783          * @param {Roo.TabPanel} this
10784          * @param {Object} e Set cancel to true on this object to cancel the tab change
10785          * @param {Roo.TabPanelItem} tab The tab being changed to
10786          */
10787         "beforetabchange" : true
10788     });
10789
10790     Roo.EventManager.onWindowResize(this.onResize, this);
10791     this.cpad = this.el.getPadding("lr");
10792     this.hiddenCount = 0;
10793
10794
10795     // toolbar on the tabbar support...
10796     if (this.toolbar) {
10797         var tcfg = this.toolbar;
10798         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10799         this.toolbar = new Roo.Toolbar(tcfg);
10800         if (Roo.isSafari) {
10801             var tbl = tcfg.container.child('table', true);
10802             tbl.setAttribute('width', '100%');
10803         }
10804         
10805     }
10806    
10807
10808
10809     Roo.TabPanel.superclass.constructor.call(this);
10810 };
10811
10812 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10813     /*
10814      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10815      */
10816     tabPosition : "top",
10817     /*
10818      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10819      */
10820     currentTabWidth : 0,
10821     /*
10822      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10823      */
10824     minTabWidth : 40,
10825     /*
10826      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10827      */
10828     maxTabWidth : 250,
10829     /*
10830      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10831      */
10832     preferredTabWidth : 175,
10833     /*
10834      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10835      */
10836     resizeTabs : false,
10837     /*
10838      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10839      */
10840     monitorResize : true,
10841     /*
10842      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10843      */
10844     toolbar : false,
10845
10846     /**
10847      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10848      * @param {String} id The id of the div to use <b>or create</b>
10849      * @param {String} text The text for the tab
10850      * @param {String} content (optional) Content to put in the TabPanelItem body
10851      * @param {Boolean} closable (optional) True to create a close icon on the tab
10852      * @return {Roo.TabPanelItem} The created TabPanelItem
10853      */
10854     addTab : function(id, text, content, closable){
10855         var item = new Roo.TabPanelItem(this, id, text, closable);
10856         this.addTabItem(item);
10857         if(content){
10858             item.setContent(content);
10859         }
10860         return item;
10861     },
10862
10863     /**
10864      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10865      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10866      * @return {Roo.TabPanelItem}
10867      */
10868     getTab : function(id){
10869         return this.items[id];
10870     },
10871
10872     /**
10873      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10874      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10875      */
10876     hideTab : function(id){
10877         var t = this.items[id];
10878         if(!t.isHidden()){
10879            t.setHidden(true);
10880            this.hiddenCount++;
10881            this.autoSizeTabs();
10882         }
10883     },
10884
10885     /**
10886      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10887      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10888      */
10889     unhideTab : function(id){
10890         var t = this.items[id];
10891         if(t.isHidden()){
10892            t.setHidden(false);
10893            this.hiddenCount--;
10894            this.autoSizeTabs();
10895         }
10896     },
10897
10898     /**
10899      * Adds an existing {@link Roo.TabPanelItem}.
10900      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10901      */
10902     addTabItem : function(item){
10903         this.items[item.id] = item;
10904         this.items.push(item);
10905         if(this.resizeTabs){
10906            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10907            this.autoSizeTabs();
10908         }else{
10909             item.autoSize();
10910         }
10911     },
10912
10913     /**
10914      * Removes a {@link Roo.TabPanelItem}.
10915      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10916      */
10917     removeTab : function(id){
10918         var items = this.items;
10919         var tab = items[id];
10920         if(!tab) { return; }
10921         var index = items.indexOf(tab);
10922         if(this.active == tab && items.length > 1){
10923             var newTab = this.getNextAvailable(index);
10924             if(newTab) {
10925                 newTab.activate();
10926             }
10927         }
10928         this.stripEl.dom.removeChild(tab.pnode.dom);
10929         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10930             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10931         }
10932         items.splice(index, 1);
10933         delete this.items[tab.id];
10934         tab.fireEvent("close", tab);
10935         tab.purgeListeners();
10936         this.autoSizeTabs();
10937     },
10938
10939     getNextAvailable : function(start){
10940         var items = this.items;
10941         var index = start;
10942         // look for a next tab that will slide over to
10943         // replace the one being removed
10944         while(index < items.length){
10945             var item = items[++index];
10946             if(item && !item.isHidden()){
10947                 return item;
10948             }
10949         }
10950         // if one isn't found select the previous tab (on the left)
10951         index = start;
10952         while(index >= 0){
10953             var item = items[--index];
10954             if(item && !item.isHidden()){
10955                 return item;
10956             }
10957         }
10958         return null;
10959     },
10960
10961     /**
10962      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10963      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10964      */
10965     disableTab : function(id){
10966         var tab = this.items[id];
10967         if(tab && this.active != tab){
10968             tab.disable();
10969         }
10970     },
10971
10972     /**
10973      * Enables a {@link Roo.TabPanelItem} that is disabled.
10974      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10975      */
10976     enableTab : function(id){
10977         var tab = this.items[id];
10978         tab.enable();
10979     },
10980
10981     /**
10982      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10983      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10984      * @return {Roo.TabPanelItem} The TabPanelItem.
10985      */
10986     activate : function(id){
10987         var tab = this.items[id];
10988         if(!tab){
10989             return null;
10990         }
10991         if(tab == this.active || tab.disabled){
10992             return tab;
10993         }
10994         var e = {};
10995         this.fireEvent("beforetabchange", this, e, tab);
10996         if(e.cancel !== true && !tab.disabled){
10997             if(this.active){
10998                 this.active.hide();
10999             }
11000             this.active = this.items[id];
11001             this.active.show();
11002             this.fireEvent("tabchange", this, this.active);
11003         }
11004         return tab;
11005     },
11006
11007     /**
11008      * Gets the active {@link Roo.TabPanelItem}.
11009      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11010      */
11011     getActiveTab : function(){
11012         return this.active;
11013     },
11014
11015     /**
11016      * Updates the tab body element to fit the height of the container element
11017      * for overflow scrolling
11018      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11019      */
11020     syncHeight : function(targetHeight){
11021         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11022         var bm = this.bodyEl.getMargins();
11023         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11024         this.bodyEl.setHeight(newHeight);
11025         return newHeight;
11026     },
11027
11028     onResize : function(){
11029         if(this.monitorResize){
11030             this.autoSizeTabs();
11031         }
11032     },
11033
11034     /**
11035      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11036      */
11037     beginUpdate : function(){
11038         this.updating = true;
11039     },
11040
11041     /**
11042      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11043      */
11044     endUpdate : function(){
11045         this.updating = false;
11046         this.autoSizeTabs();
11047     },
11048
11049     /**
11050      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11051      */
11052     autoSizeTabs : function(){
11053         var count = this.items.length;
11054         var vcount = count - this.hiddenCount;
11055         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11056         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11057         var availWidth = Math.floor(w / vcount);
11058         var b = this.stripBody;
11059         if(b.getWidth() > w){
11060             var tabs = this.items;
11061             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11062             if(availWidth < this.minTabWidth){
11063                 /*if(!this.sleft){    // incomplete scrolling code
11064                     this.createScrollButtons();
11065                 }
11066                 this.showScroll();
11067                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11068             }
11069         }else{
11070             if(this.currentTabWidth < this.preferredTabWidth){
11071                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11072             }
11073         }
11074     },
11075
11076     /**
11077      * Returns the number of tabs in this TabPanel.
11078      * @return {Number}
11079      */
11080      getCount : function(){
11081          return this.items.length;
11082      },
11083
11084     /**
11085      * Resizes all the tabs to the passed width
11086      * @param {Number} The new width
11087      */
11088     setTabWidth : function(width){
11089         this.currentTabWidth = width;
11090         for(var i = 0, len = this.items.length; i < len; i++) {
11091                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11092         }
11093     },
11094
11095     /**
11096      * Destroys this TabPanel
11097      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11098      */
11099     destroy : function(removeEl){
11100         Roo.EventManager.removeResizeListener(this.onResize, this);
11101         for(var i = 0, len = this.items.length; i < len; i++){
11102             this.items[i].purgeListeners();
11103         }
11104         if(removeEl === true){
11105             this.el.update("");
11106             this.el.remove();
11107         }
11108     }
11109 });
11110
11111 /**
11112  * @class Roo.TabPanelItem
11113  * @extends Roo.util.Observable
11114  * Represents an individual item (tab plus body) in a TabPanel.
11115  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11116  * @param {String} id The id of this TabPanelItem
11117  * @param {String} text The text for the tab of this TabPanelItem
11118  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11119  */
11120 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11121     /**
11122      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11123      * @type Roo.TabPanel
11124      */
11125     this.tabPanel = tabPanel;
11126     /**
11127      * The id for this TabPanelItem
11128      * @type String
11129      */
11130     this.id = id;
11131     /** @private */
11132     this.disabled = false;
11133     /** @private */
11134     this.text = text;
11135     /** @private */
11136     this.loaded = false;
11137     this.closable = closable;
11138
11139     /**
11140      * The body element for this TabPanelItem.
11141      * @type Roo.Element
11142      */
11143     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11144     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11145     this.bodyEl.setStyle("display", "block");
11146     this.bodyEl.setStyle("zoom", "1");
11147     this.hideAction();
11148
11149     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11150     /** @private */
11151     this.el = Roo.get(els.el, true);
11152     this.inner = Roo.get(els.inner, true);
11153     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11154     this.pnode = Roo.get(els.el.parentNode, true);
11155     this.el.on("mousedown", this.onTabMouseDown, this);
11156     this.el.on("click", this.onTabClick, this);
11157     /** @private */
11158     if(closable){
11159         var c = Roo.get(els.close, true);
11160         c.dom.title = this.closeText;
11161         c.addClassOnOver("close-over");
11162         c.on("click", this.closeClick, this);
11163      }
11164
11165     this.addEvents({
11166          /**
11167          * @event activate
11168          * Fires when this tab becomes the active tab.
11169          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11170          * @param {Roo.TabPanelItem} this
11171          */
11172         "activate": true,
11173         /**
11174          * @event beforeclose
11175          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11176          * @param {Roo.TabPanelItem} this
11177          * @param {Object} e Set cancel to true on this object to cancel the close.
11178          */
11179         "beforeclose": true,
11180         /**
11181          * @event close
11182          * Fires when this tab is closed.
11183          * @param {Roo.TabPanelItem} this
11184          */
11185          "close": true,
11186         /**
11187          * @event deactivate
11188          * Fires when this tab is no longer the active tab.
11189          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11190          * @param {Roo.TabPanelItem} this
11191          */
11192          "deactivate" : true
11193     });
11194     this.hidden = false;
11195
11196     Roo.TabPanelItem.superclass.constructor.call(this);
11197 };
11198
11199 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11200     purgeListeners : function(){
11201        Roo.util.Observable.prototype.purgeListeners.call(this);
11202        this.el.removeAllListeners();
11203     },
11204     /**
11205      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11206      */
11207     show : function(){
11208         this.pnode.addClass("on");
11209         this.showAction();
11210         if(Roo.isOpera){
11211             this.tabPanel.stripWrap.repaint();
11212         }
11213         this.fireEvent("activate", this.tabPanel, this);
11214     },
11215
11216     /**
11217      * Returns true if this tab is the active tab.
11218      * @return {Boolean}
11219      */
11220     isActive : function(){
11221         return this.tabPanel.getActiveTab() == this;
11222     },
11223
11224     /**
11225      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11226      */
11227     hide : function(){
11228         this.pnode.removeClass("on");
11229         this.hideAction();
11230         this.fireEvent("deactivate", this.tabPanel, this);
11231     },
11232
11233     hideAction : function(){
11234         this.bodyEl.hide();
11235         this.bodyEl.setStyle("position", "absolute");
11236         this.bodyEl.setLeft("-20000px");
11237         this.bodyEl.setTop("-20000px");
11238     },
11239
11240     showAction : function(){
11241         this.bodyEl.setStyle("position", "relative");
11242         this.bodyEl.setTop("");
11243         this.bodyEl.setLeft("");
11244         this.bodyEl.show();
11245     },
11246
11247     /**
11248      * Set the tooltip for the tab.
11249      * @param {String} tooltip The tab's tooltip
11250      */
11251     setTooltip : function(text){
11252         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11253             this.textEl.dom.qtip = text;
11254             this.textEl.dom.removeAttribute('title');
11255         }else{
11256             this.textEl.dom.title = text;
11257         }
11258     },
11259
11260     onTabClick : function(e){
11261         e.preventDefault();
11262         this.tabPanel.activate(this.id);
11263     },
11264
11265     onTabMouseDown : function(e){
11266         e.preventDefault();
11267         this.tabPanel.activate(this.id);
11268     },
11269
11270     getWidth : function(){
11271         return this.inner.getWidth();
11272     },
11273
11274     setWidth : function(width){
11275         var iwidth = width - this.pnode.getPadding("lr");
11276         this.inner.setWidth(iwidth);
11277         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11278         this.pnode.setWidth(width);
11279     },
11280
11281     /**
11282      * Show or hide the tab
11283      * @param {Boolean} hidden True to hide or false to show.
11284      */
11285     setHidden : function(hidden){
11286         this.hidden = hidden;
11287         this.pnode.setStyle("display", hidden ? "none" : "");
11288     },
11289
11290     /**
11291      * Returns true if this tab is "hidden"
11292      * @return {Boolean}
11293      */
11294     isHidden : function(){
11295         return this.hidden;
11296     },
11297
11298     /**
11299      * Returns the text for this tab
11300      * @return {String}
11301      */
11302     getText : function(){
11303         return this.text;
11304     },
11305
11306     autoSize : function(){
11307         //this.el.beginMeasure();
11308         this.textEl.setWidth(1);
11309         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11310         //this.el.endMeasure();
11311     },
11312
11313     /**
11314      * Sets the text for the tab (Note: this also sets the tooltip text)
11315      * @param {String} text The tab's text and tooltip
11316      */
11317     setText : function(text){
11318         this.text = text;
11319         this.textEl.update(text);
11320         this.setTooltip(text);
11321         if(!this.tabPanel.resizeTabs){
11322             this.autoSize();
11323         }
11324     },
11325     /**
11326      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11327      */
11328     activate : function(){
11329         this.tabPanel.activate(this.id);
11330     },
11331
11332     /**
11333      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11334      */
11335     disable : function(){
11336         if(this.tabPanel.active != this){
11337             this.disabled = true;
11338             this.pnode.addClass("disabled");
11339         }
11340     },
11341
11342     /**
11343      * Enables this TabPanelItem if it was previously disabled.
11344      */
11345     enable : function(){
11346         this.disabled = false;
11347         this.pnode.removeClass("disabled");
11348     },
11349
11350     /**
11351      * Sets the content for this TabPanelItem.
11352      * @param {String} content The content
11353      * @param {Boolean} loadScripts true to look for and load scripts
11354      */
11355     setContent : function(content, loadScripts){
11356         this.bodyEl.update(content, loadScripts);
11357     },
11358
11359     /**
11360      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11361      * @return {Roo.UpdateManager} The UpdateManager
11362      */
11363     getUpdateManager : function(){
11364         return this.bodyEl.getUpdateManager();
11365     },
11366
11367     /**
11368      * Set a URL to be used to load the content for this TabPanelItem.
11369      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11370      * @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)
11371      * @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)
11372      * @return {Roo.UpdateManager} The UpdateManager
11373      */
11374     setUrl : function(url, params, loadOnce){
11375         if(this.refreshDelegate){
11376             this.un('activate', this.refreshDelegate);
11377         }
11378         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11379         this.on("activate", this.refreshDelegate);
11380         return this.bodyEl.getUpdateManager();
11381     },
11382
11383     /** @private */
11384     _handleRefresh : function(url, params, loadOnce){
11385         if(!loadOnce || !this.loaded){
11386             var updater = this.bodyEl.getUpdateManager();
11387             updater.update(url, params, this._setLoaded.createDelegate(this));
11388         }
11389     },
11390
11391     /**
11392      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11393      *   Will fail silently if the setUrl method has not been called.
11394      *   This does not activate the panel, just updates its content.
11395      */
11396     refresh : function(){
11397         if(this.refreshDelegate){
11398            this.loaded = false;
11399            this.refreshDelegate();
11400         }
11401     },
11402
11403     /** @private */
11404     _setLoaded : function(){
11405         this.loaded = true;
11406     },
11407
11408     /** @private */
11409     closeClick : function(e){
11410         var o = {};
11411         e.stopEvent();
11412         this.fireEvent("beforeclose", this, o);
11413         if(o.cancel !== true){
11414             this.tabPanel.removeTab(this.id);
11415         }
11416     },
11417     /**
11418      * The text displayed in the tooltip for the close icon.
11419      * @type String
11420      */
11421     closeText : "Close this tab"
11422 });
11423
11424 /** @private */
11425 Roo.TabPanel.prototype.createStrip = function(container){
11426     var strip = document.createElement("div");
11427     strip.className = "x-tabs-wrap";
11428     container.appendChild(strip);
11429     return strip;
11430 };
11431 /** @private */
11432 Roo.TabPanel.prototype.createStripList = function(strip){
11433     // div wrapper for retard IE
11434     // returns the "tr" element.
11435     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11436         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11437         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11438     return strip.firstChild.firstChild.firstChild.firstChild;
11439 };
11440 /** @private */
11441 Roo.TabPanel.prototype.createBody = function(container){
11442     var body = document.createElement("div");
11443     Roo.id(body, "tab-body");
11444     Roo.fly(body).addClass("x-tabs-body");
11445     container.appendChild(body);
11446     return body;
11447 };
11448 /** @private */
11449 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11450     var body = Roo.getDom(id);
11451     if(!body){
11452         body = document.createElement("div");
11453         body.id = id;
11454     }
11455     Roo.fly(body).addClass("x-tabs-item-body");
11456     bodyEl.insertBefore(body, bodyEl.firstChild);
11457     return body;
11458 };
11459 /** @private */
11460 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11461     var td = document.createElement("td");
11462     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11463     //stripEl.appendChild(td);
11464     if(closable){
11465         td.className = "x-tabs-closable";
11466         if(!this.closeTpl){
11467             this.closeTpl = new Roo.Template(
11468                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11469                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11470                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11471             );
11472         }
11473         var el = this.closeTpl.overwrite(td, {"text": text});
11474         var close = el.getElementsByTagName("div")[0];
11475         var inner = el.getElementsByTagName("em")[0];
11476         return {"el": el, "close": close, "inner": inner};
11477     } else {
11478         if(!this.tabTpl){
11479             this.tabTpl = new Roo.Template(
11480                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11481                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11482             );
11483         }
11484         var el = this.tabTpl.overwrite(td, {"text": text});
11485         var inner = el.getElementsByTagName("em")[0];
11486         return {"el": el, "inner": inner};
11487     }
11488 };/*
11489  * Based on:
11490  * Ext JS Library 1.1.1
11491  * Copyright(c) 2006-2007, Ext JS, LLC.
11492  *
11493  * Originally Released Under LGPL - original licence link has changed is not relivant.
11494  *
11495  * Fork - LGPL
11496  * <script type="text/javascript">
11497  */
11498
11499 /**
11500  * @class Roo.Button
11501  * @extends Roo.util.Observable
11502  * Simple Button class
11503  * @cfg {String} text The button text
11504  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11505  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11506  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11507  * @cfg {Object} scope The scope of the handler
11508  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11509  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11510  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11511  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11512  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11513  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11514    applies if enableToggle = true)
11515  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11516  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11517   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11518  * @constructor
11519  * Create a new button
11520  * @param {Object} config The config object
11521  */
11522 Roo.Button = function(renderTo, config)
11523 {
11524     if (!config) {
11525         config = renderTo;
11526         renderTo = config.renderTo || false;
11527     }
11528     
11529     Roo.apply(this, config);
11530     this.addEvents({
11531         /**
11532              * @event click
11533              * Fires when this button is clicked
11534              * @param {Button} this
11535              * @param {EventObject} e The click event
11536              */
11537             "click" : true,
11538         /**
11539              * @event toggle
11540              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11541              * @param {Button} this
11542              * @param {Boolean} pressed
11543              */
11544             "toggle" : true,
11545         /**
11546              * @event mouseover
11547              * Fires when the mouse hovers over the button
11548              * @param {Button} this
11549              * @param {Event} e The event object
11550              */
11551         'mouseover' : true,
11552         /**
11553              * @event mouseout
11554              * Fires when the mouse exits the button
11555              * @param {Button} this
11556              * @param {Event} e The event object
11557              */
11558         'mouseout': true,
11559          /**
11560              * @event render
11561              * Fires when the button is rendered
11562              * @param {Button} this
11563              */
11564         'render': true
11565     });
11566     if(this.menu){
11567         this.menu = Roo.menu.MenuMgr.get(this.menu);
11568     }
11569     // register listeners first!!  - so render can be captured..
11570     Roo.util.Observable.call(this);
11571     if(renderTo){
11572         this.render(renderTo);
11573     }
11574     
11575   
11576 };
11577
11578 Roo.extend(Roo.Button, Roo.util.Observable, {
11579     /**
11580      * 
11581      */
11582     
11583     /**
11584      * Read-only. True if this button is hidden
11585      * @type Boolean
11586      */
11587     hidden : false,
11588     /**
11589      * Read-only. True if this button is disabled
11590      * @type Boolean
11591      */
11592     disabled : false,
11593     /**
11594      * Read-only. True if this button is pressed (only if enableToggle = true)
11595      * @type Boolean
11596      */
11597     pressed : false,
11598
11599     /**
11600      * @cfg {Number} tabIndex 
11601      * The DOM tabIndex for this button (defaults to undefined)
11602      */
11603     tabIndex : undefined,
11604
11605     /**
11606      * @cfg {Boolean} enableToggle
11607      * True to enable pressed/not pressed toggling (defaults to false)
11608      */
11609     enableToggle: false,
11610     /**
11611      * @cfg {Mixed} menu
11612      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11613      */
11614     menu : undefined,
11615     /**
11616      * @cfg {String} menuAlign
11617      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11618      */
11619     menuAlign : "tl-bl?",
11620
11621     /**
11622      * @cfg {String} iconCls
11623      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11624      */
11625     iconCls : undefined,
11626     /**
11627      * @cfg {String} type
11628      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11629      */
11630     type : 'button',
11631
11632     // private
11633     menuClassTarget: 'tr',
11634
11635     /**
11636      * @cfg {String} clickEvent
11637      * The type of event to map to the button's event handler (defaults to 'click')
11638      */
11639     clickEvent : 'click',
11640
11641     /**
11642      * @cfg {Boolean} handleMouseEvents
11643      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11644      */
11645     handleMouseEvents : true,
11646
11647     /**
11648      * @cfg {String} tooltipType
11649      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11650      */
11651     tooltipType : 'qtip',
11652
11653     /**
11654      * @cfg {String} cls
11655      * A CSS class to apply to the button's main element.
11656      */
11657     
11658     /**
11659      * @cfg {Roo.Template} template (Optional)
11660      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11661      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11662      * require code modifications if required elements (e.g. a button) aren't present.
11663      */
11664
11665     // private
11666     render : function(renderTo){
11667         var btn;
11668         if(this.hideParent){
11669             this.parentEl = Roo.get(renderTo);
11670         }
11671         if(!this.dhconfig){
11672             if(!this.template){
11673                 if(!Roo.Button.buttonTemplate){
11674                     // hideous table template
11675                     Roo.Button.buttonTemplate = new Roo.Template(
11676                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11677                         '<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>',
11678                         "</tr></tbody></table>");
11679                 }
11680                 this.template = Roo.Button.buttonTemplate;
11681             }
11682             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11683             var btnEl = btn.child("button:first");
11684             btnEl.on('focus', this.onFocus, this);
11685             btnEl.on('blur', this.onBlur, this);
11686             if(this.cls){
11687                 btn.addClass(this.cls);
11688             }
11689             if(this.icon){
11690                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11691             }
11692             if(this.iconCls){
11693                 btnEl.addClass(this.iconCls);
11694                 if(!this.cls){
11695                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11696                 }
11697             }
11698             if(this.tabIndex !== undefined){
11699                 btnEl.dom.tabIndex = this.tabIndex;
11700             }
11701             if(this.tooltip){
11702                 if(typeof this.tooltip == 'object'){
11703                     Roo.QuickTips.tips(Roo.apply({
11704                           target: btnEl.id
11705                     }, this.tooltip));
11706                 } else {
11707                     btnEl.dom[this.tooltipType] = this.tooltip;
11708                 }
11709             }
11710         }else{
11711             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11712         }
11713         this.el = btn;
11714         if(this.id){
11715             this.el.dom.id = this.el.id = this.id;
11716         }
11717         if(this.menu){
11718             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11719             this.menu.on("show", this.onMenuShow, this);
11720             this.menu.on("hide", this.onMenuHide, this);
11721         }
11722         btn.addClass("x-btn");
11723         if(Roo.isIE && !Roo.isIE7){
11724             this.autoWidth.defer(1, this);
11725         }else{
11726             this.autoWidth();
11727         }
11728         if(this.handleMouseEvents){
11729             btn.on("mouseover", this.onMouseOver, this);
11730             btn.on("mouseout", this.onMouseOut, this);
11731             btn.on("mousedown", this.onMouseDown, this);
11732         }
11733         btn.on(this.clickEvent, this.onClick, this);
11734         //btn.on("mouseup", this.onMouseUp, this);
11735         if(this.hidden){
11736             this.hide();
11737         }
11738         if(this.disabled){
11739             this.disable();
11740         }
11741         Roo.ButtonToggleMgr.register(this);
11742         if(this.pressed){
11743             this.el.addClass("x-btn-pressed");
11744         }
11745         if(this.repeat){
11746             var repeater = new Roo.util.ClickRepeater(btn,
11747                 typeof this.repeat == "object" ? this.repeat : {}
11748             );
11749             repeater.on("click", this.onClick,  this);
11750         }
11751         
11752         this.fireEvent('render', this);
11753         
11754     },
11755     /**
11756      * Returns the button's underlying element
11757      * @return {Roo.Element} The element
11758      */
11759     getEl : function(){
11760         return this.el;  
11761     },
11762     
11763     /**
11764      * Destroys this Button and removes any listeners.
11765      */
11766     destroy : function(){
11767         Roo.ButtonToggleMgr.unregister(this);
11768         this.el.removeAllListeners();
11769         this.purgeListeners();
11770         this.el.remove();
11771     },
11772
11773     // private
11774     autoWidth : function(){
11775         if(this.el){
11776             this.el.setWidth("auto");
11777             if(Roo.isIE7 && Roo.isStrict){
11778                 var ib = this.el.child('button');
11779                 if(ib && ib.getWidth() > 20){
11780                     ib.clip();
11781                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11782                 }
11783             }
11784             if(this.minWidth){
11785                 if(this.hidden){
11786                     this.el.beginMeasure();
11787                 }
11788                 if(this.el.getWidth() < this.minWidth){
11789                     this.el.setWidth(this.minWidth);
11790                 }
11791                 if(this.hidden){
11792                     this.el.endMeasure();
11793                 }
11794             }
11795         }
11796     },
11797
11798     /**
11799      * Assigns this button's click handler
11800      * @param {Function} handler The function to call when the button is clicked
11801      * @param {Object} scope (optional) Scope for the function passed in
11802      */
11803     setHandler : function(handler, scope){
11804         this.handler = handler;
11805         this.scope = scope;  
11806     },
11807     
11808     /**
11809      * Sets this button's text
11810      * @param {String} text The button text
11811      */
11812     setText : function(text){
11813         this.text = text;
11814         if(this.el){
11815             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11816         }
11817         this.autoWidth();
11818     },
11819     
11820     /**
11821      * Gets the text for this button
11822      * @return {String} The button text
11823      */
11824     getText : function(){
11825         return this.text;  
11826     },
11827     
11828     /**
11829      * Show this button
11830      */
11831     show: function(){
11832         this.hidden = false;
11833         if(this.el){
11834             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11835         }
11836     },
11837     
11838     /**
11839      * Hide this button
11840      */
11841     hide: function(){
11842         this.hidden = true;
11843         if(this.el){
11844             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11845         }
11846     },
11847     
11848     /**
11849      * Convenience function for boolean show/hide
11850      * @param {Boolean} visible True to show, false to hide
11851      */
11852     setVisible: function(visible){
11853         if(visible) {
11854             this.show();
11855         }else{
11856             this.hide();
11857         }
11858     },
11859     
11860     /**
11861      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11862      * @param {Boolean} state (optional) Force a particular state
11863      */
11864     toggle : function(state){
11865         state = state === undefined ? !this.pressed : state;
11866         if(state != this.pressed){
11867             if(state){
11868                 this.el.addClass("x-btn-pressed");
11869                 this.pressed = true;
11870                 this.fireEvent("toggle", this, true);
11871             }else{
11872                 this.el.removeClass("x-btn-pressed");
11873                 this.pressed = false;
11874                 this.fireEvent("toggle", this, false);
11875             }
11876             if(this.toggleHandler){
11877                 this.toggleHandler.call(this.scope || this, this, state);
11878             }
11879         }
11880     },
11881     
11882     /**
11883      * Focus the button
11884      */
11885     focus : function(){
11886         this.el.child('button:first').focus();
11887     },
11888     
11889     /**
11890      * Disable this button
11891      */
11892     disable : function(){
11893         if(this.el){
11894             this.el.addClass("x-btn-disabled");
11895         }
11896         this.disabled = true;
11897     },
11898     
11899     /**
11900      * Enable this button
11901      */
11902     enable : function(){
11903         if(this.el){
11904             this.el.removeClass("x-btn-disabled");
11905         }
11906         this.disabled = false;
11907     },
11908
11909     /**
11910      * Convenience function for boolean enable/disable
11911      * @param {Boolean} enabled True to enable, false to disable
11912      */
11913     setDisabled : function(v){
11914         this[v !== true ? "enable" : "disable"]();
11915     },
11916
11917     // private
11918     onClick : function(e){
11919         if(e){
11920             e.preventDefault();
11921         }
11922         if(e.button != 0){
11923             return;
11924         }
11925         if(!this.disabled){
11926             if(this.enableToggle){
11927                 this.toggle();
11928             }
11929             if(this.menu && !this.menu.isVisible()){
11930                 this.menu.show(this.el, this.menuAlign);
11931             }
11932             this.fireEvent("click", this, e);
11933             if(this.handler){
11934                 this.el.removeClass("x-btn-over");
11935                 this.handler.call(this.scope || this, this, e);
11936             }
11937         }
11938     },
11939     // private
11940     onMouseOver : function(e){
11941         if(!this.disabled){
11942             this.el.addClass("x-btn-over");
11943             this.fireEvent('mouseover', this, e);
11944         }
11945     },
11946     // private
11947     onMouseOut : function(e){
11948         if(!e.within(this.el,  true)){
11949             this.el.removeClass("x-btn-over");
11950             this.fireEvent('mouseout', this, e);
11951         }
11952     },
11953     // private
11954     onFocus : function(e){
11955         if(!this.disabled){
11956             this.el.addClass("x-btn-focus");
11957         }
11958     },
11959     // private
11960     onBlur : function(e){
11961         this.el.removeClass("x-btn-focus");
11962     },
11963     // private
11964     onMouseDown : function(e){
11965         if(!this.disabled && e.button == 0){
11966             this.el.addClass("x-btn-click");
11967             Roo.get(document).on('mouseup', this.onMouseUp, this);
11968         }
11969     },
11970     // private
11971     onMouseUp : function(e){
11972         if(e.button == 0){
11973             this.el.removeClass("x-btn-click");
11974             Roo.get(document).un('mouseup', this.onMouseUp, this);
11975         }
11976     },
11977     // private
11978     onMenuShow : function(e){
11979         this.el.addClass("x-btn-menu-active");
11980     },
11981     // private
11982     onMenuHide : function(e){
11983         this.el.removeClass("x-btn-menu-active");
11984     }   
11985 });
11986
11987 // Private utility class used by Button
11988 Roo.ButtonToggleMgr = function(){
11989    var groups = {};
11990    
11991    function toggleGroup(btn, state){
11992        if(state){
11993            var g = groups[btn.toggleGroup];
11994            for(var i = 0, l = g.length; i < l; i++){
11995                if(g[i] != btn){
11996                    g[i].toggle(false);
11997                }
11998            }
11999        }
12000    }
12001    
12002    return {
12003        register : function(btn){
12004            if(!btn.toggleGroup){
12005                return;
12006            }
12007            var g = groups[btn.toggleGroup];
12008            if(!g){
12009                g = groups[btn.toggleGroup] = [];
12010            }
12011            g.push(btn);
12012            btn.on("toggle", toggleGroup);
12013        },
12014        
12015        unregister : function(btn){
12016            if(!btn.toggleGroup){
12017                return;
12018            }
12019            var g = groups[btn.toggleGroup];
12020            if(g){
12021                g.remove(btn);
12022                btn.un("toggle", toggleGroup);
12023            }
12024        }
12025    };
12026 }();/*
12027  * Based on:
12028  * Ext JS Library 1.1.1
12029  * Copyright(c) 2006-2007, Ext JS, LLC.
12030  *
12031  * Originally Released Under LGPL - original licence link has changed is not relivant.
12032  *
12033  * Fork - LGPL
12034  * <script type="text/javascript">
12035  */
12036  
12037 /**
12038  * @class Roo.SplitButton
12039  * @extends Roo.Button
12040  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12041  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12042  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12043  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12044  * @cfg {String} arrowTooltip The title attribute of the arrow
12045  * @constructor
12046  * Create a new menu button
12047  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12048  * @param {Object} config The config object
12049  */
12050 Roo.SplitButton = function(renderTo, config){
12051     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12052     /**
12053      * @event arrowclick
12054      * Fires when this button's arrow is clicked
12055      * @param {SplitButton} this
12056      * @param {EventObject} e The click event
12057      */
12058     this.addEvents({"arrowclick":true});
12059 };
12060
12061 Roo.extend(Roo.SplitButton, Roo.Button, {
12062     render : function(renderTo){
12063         // this is one sweet looking template!
12064         var tpl = new Roo.Template(
12065             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12066             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12067             '<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>',
12068             "</tbody></table></td><td>",
12069             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12070             '<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>',
12071             "</tbody></table></td></tr></table>"
12072         );
12073         var btn = tpl.append(renderTo, [this.text, this.type], true);
12074         var btnEl = btn.child("button");
12075         if(this.cls){
12076             btn.addClass(this.cls);
12077         }
12078         if(this.icon){
12079             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12080         }
12081         if(this.iconCls){
12082             btnEl.addClass(this.iconCls);
12083             if(!this.cls){
12084                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12085             }
12086         }
12087         this.el = btn;
12088         if(this.handleMouseEvents){
12089             btn.on("mouseover", this.onMouseOver, this);
12090             btn.on("mouseout", this.onMouseOut, this);
12091             btn.on("mousedown", this.onMouseDown, this);
12092             btn.on("mouseup", this.onMouseUp, this);
12093         }
12094         btn.on(this.clickEvent, this.onClick, this);
12095         if(this.tooltip){
12096             if(typeof this.tooltip == 'object'){
12097                 Roo.QuickTips.tips(Roo.apply({
12098                       target: btnEl.id
12099                 }, this.tooltip));
12100             } else {
12101                 btnEl.dom[this.tooltipType] = this.tooltip;
12102             }
12103         }
12104         if(this.arrowTooltip){
12105             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12106         }
12107         if(this.hidden){
12108             this.hide();
12109         }
12110         if(this.disabled){
12111             this.disable();
12112         }
12113         if(this.pressed){
12114             this.el.addClass("x-btn-pressed");
12115         }
12116         if(Roo.isIE && !Roo.isIE7){
12117             this.autoWidth.defer(1, this);
12118         }else{
12119             this.autoWidth();
12120         }
12121         if(this.menu){
12122             this.menu.on("show", this.onMenuShow, this);
12123             this.menu.on("hide", this.onMenuHide, this);
12124         }
12125         this.fireEvent('render', this);
12126     },
12127
12128     // private
12129     autoWidth : function(){
12130         if(this.el){
12131             var tbl = this.el.child("table:first");
12132             var tbl2 = this.el.child("table:last");
12133             this.el.setWidth("auto");
12134             tbl.setWidth("auto");
12135             if(Roo.isIE7 && Roo.isStrict){
12136                 var ib = this.el.child('button:first');
12137                 if(ib && ib.getWidth() > 20){
12138                     ib.clip();
12139                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12140                 }
12141             }
12142             if(this.minWidth){
12143                 if(this.hidden){
12144                     this.el.beginMeasure();
12145                 }
12146                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12147                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12148                 }
12149                 if(this.hidden){
12150                     this.el.endMeasure();
12151                 }
12152             }
12153             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12154         } 
12155     },
12156     /**
12157      * Sets this button's click handler
12158      * @param {Function} handler The function to call when the button is clicked
12159      * @param {Object} scope (optional) Scope for the function passed above
12160      */
12161     setHandler : function(handler, scope){
12162         this.handler = handler;
12163         this.scope = scope;  
12164     },
12165     
12166     /**
12167      * Sets this button's arrow click handler
12168      * @param {Function} handler The function to call when the arrow is clicked
12169      * @param {Object} scope (optional) Scope for the function passed above
12170      */
12171     setArrowHandler : function(handler, scope){
12172         this.arrowHandler = handler;
12173         this.scope = scope;  
12174     },
12175     
12176     /**
12177      * Focus the button
12178      */
12179     focus : function(){
12180         if(this.el){
12181             this.el.child("button:first").focus();
12182         }
12183     },
12184
12185     // private
12186     onClick : function(e){
12187         e.preventDefault();
12188         if(!this.disabled){
12189             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12190                 if(this.menu && !this.menu.isVisible()){
12191                     this.menu.show(this.el, this.menuAlign);
12192                 }
12193                 this.fireEvent("arrowclick", this, e);
12194                 if(this.arrowHandler){
12195                     this.arrowHandler.call(this.scope || this, this, e);
12196                 }
12197             }else{
12198                 this.fireEvent("click", this, e);
12199                 if(this.handler){
12200                     this.handler.call(this.scope || this, this, e);
12201                 }
12202             }
12203         }
12204     },
12205     // private
12206     onMouseDown : function(e){
12207         if(!this.disabled){
12208             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12209         }
12210     },
12211     // private
12212     onMouseUp : function(e){
12213         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12214     }   
12215 });
12216
12217
12218 // backwards compat
12219 Roo.MenuButton = Roo.SplitButton;/*
12220  * Based on:
12221  * Ext JS Library 1.1.1
12222  * Copyright(c) 2006-2007, Ext JS, LLC.
12223  *
12224  * Originally Released Under LGPL - original licence link has changed is not relivant.
12225  *
12226  * Fork - LGPL
12227  * <script type="text/javascript">
12228  */
12229
12230 /**
12231  * @class Roo.Toolbar
12232  * Basic Toolbar class.
12233  * @constructor
12234  * Creates a new Toolbar
12235  * @param {Object} config The config object
12236  */ 
12237 Roo.Toolbar = function(container, buttons, config)
12238 {
12239     /// old consturctor format still supported..
12240     if(container instanceof Array){ // omit the container for later rendering
12241         buttons = container;
12242         config = buttons;
12243         container = null;
12244     }
12245     if (typeof(container) == 'object' && container.xtype) {
12246         config = container;
12247         container = config.container;
12248         buttons = config.buttons; // not really - use items!!
12249     }
12250     var xitems = [];
12251     if (config && config.items) {
12252         xitems = config.items;
12253         delete config.items;
12254     }
12255     Roo.apply(this, config);
12256     this.buttons = buttons;
12257     
12258     if(container){
12259         this.render(container);
12260     }
12261     Roo.each(xitems, function(b) {
12262         this.add(b);
12263     }, this);
12264     
12265 };
12266
12267 Roo.Toolbar.prototype = {
12268     /**
12269      * @cfg {Roo.data.Store} items
12270      * array of button configs or elements to add
12271      */
12272     
12273     /**
12274      * @cfg {String/HTMLElement/Element} container
12275      * The id or element that will contain the toolbar
12276      */
12277     // private
12278     render : function(ct){
12279         this.el = Roo.get(ct);
12280         if(this.cls){
12281             this.el.addClass(this.cls);
12282         }
12283         // using a table allows for vertical alignment
12284         // 100% width is needed by Safari...
12285         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12286         this.tr = this.el.child("tr", true);
12287         var autoId = 0;
12288         this.items = new Roo.util.MixedCollection(false, function(o){
12289             return o.id || ("item" + (++autoId));
12290         });
12291         if(this.buttons){
12292             this.add.apply(this, this.buttons);
12293             delete this.buttons;
12294         }
12295     },
12296
12297     /**
12298      * Adds element(s) to the toolbar -- this function takes a variable number of 
12299      * arguments of mixed type and adds them to the toolbar.
12300      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12301      * <ul>
12302      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12303      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12304      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12305      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12306      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12307      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12308      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12309      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12310      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12311      * </ul>
12312      * @param {Mixed} arg2
12313      * @param {Mixed} etc.
12314      */
12315     add : function(){
12316         var a = arguments, l = a.length;
12317         for(var i = 0; i < l; i++){
12318             this._add(a[i]);
12319         }
12320     },
12321     // private..
12322     _add : function(el) {
12323         
12324         if (el.xtype) {
12325             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12326         }
12327         
12328         if (el.applyTo){ // some kind of form field
12329             return this.addField(el);
12330         } 
12331         if (el.render){ // some kind of Toolbar.Item
12332             return this.addItem(el);
12333         }
12334         if (typeof el == "string"){ // string
12335             if(el == "separator" || el == "-"){
12336                 return this.addSeparator();
12337             }
12338             if (el == " "){
12339                 return this.addSpacer();
12340             }
12341             if(el == "->"){
12342                 return this.addFill();
12343             }
12344             return this.addText(el);
12345             
12346         }
12347         if(el.tagName){ // element
12348             return this.addElement(el);
12349         }
12350         if(typeof el == "object"){ // must be button config?
12351             return this.addButton(el);
12352         }
12353         // and now what?!?!
12354         return false;
12355         
12356     },
12357     
12358     /**
12359      * Add an Xtype element
12360      * @param {Object} xtype Xtype Object
12361      * @return {Object} created Object
12362      */
12363     addxtype : function(e){
12364         return this.add(e);  
12365     },
12366     
12367     /**
12368      * Returns the Element for this toolbar.
12369      * @return {Roo.Element}
12370      */
12371     getEl : function(){
12372         return this.el;  
12373     },
12374     
12375     /**
12376      * Adds a separator
12377      * @return {Roo.Toolbar.Item} The separator item
12378      */
12379     addSeparator : function(){
12380         return this.addItem(new Roo.Toolbar.Separator());
12381     },
12382
12383     /**
12384      * Adds a spacer element
12385      * @return {Roo.Toolbar.Spacer} The spacer item
12386      */
12387     addSpacer : function(){
12388         return this.addItem(new Roo.Toolbar.Spacer());
12389     },
12390
12391     /**
12392      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12393      * @return {Roo.Toolbar.Fill} The fill item
12394      */
12395     addFill : function(){
12396         return this.addItem(new Roo.Toolbar.Fill());
12397     },
12398
12399     /**
12400      * Adds any standard HTML element to the toolbar
12401      * @param {String/HTMLElement/Element} el The element or id of the element to add
12402      * @return {Roo.Toolbar.Item} The element's item
12403      */
12404     addElement : function(el){
12405         return this.addItem(new Roo.Toolbar.Item(el));
12406     },
12407     /**
12408      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12409      * @type Roo.util.MixedCollection  
12410      */
12411     items : false,
12412      
12413     /**
12414      * Adds any Toolbar.Item or subclass
12415      * @param {Roo.Toolbar.Item} item
12416      * @return {Roo.Toolbar.Item} The item
12417      */
12418     addItem : function(item){
12419         var td = this.nextBlock();
12420         item.render(td);
12421         this.items.add(item);
12422         return item;
12423     },
12424     
12425     /**
12426      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12427      * @param {Object/Array} config A button config or array of configs
12428      * @return {Roo.Toolbar.Button/Array}
12429      */
12430     addButton : function(config){
12431         if(config instanceof Array){
12432             var buttons = [];
12433             for(var i = 0, len = config.length; i < len; i++) {
12434                 buttons.push(this.addButton(config[i]));
12435             }
12436             return buttons;
12437         }
12438         var b = config;
12439         if(!(config instanceof Roo.Toolbar.Button)){
12440             b = config.split ?
12441                 new Roo.Toolbar.SplitButton(config) :
12442                 new Roo.Toolbar.Button(config);
12443         }
12444         var td = this.nextBlock();
12445         b.render(td);
12446         this.items.add(b);
12447         return b;
12448     },
12449     
12450     /**
12451      * Adds text to the toolbar
12452      * @param {String} text The text to add
12453      * @return {Roo.Toolbar.Item} The element's item
12454      */
12455     addText : function(text){
12456         return this.addItem(new Roo.Toolbar.TextItem(text));
12457     },
12458     
12459     /**
12460      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12461      * @param {Number} index The index where the item is to be inserted
12462      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12463      * @return {Roo.Toolbar.Button/Item}
12464      */
12465     insertButton : function(index, item){
12466         if(item instanceof Array){
12467             var buttons = [];
12468             for(var i = 0, len = item.length; i < len; i++) {
12469                buttons.push(this.insertButton(index + i, item[i]));
12470             }
12471             return buttons;
12472         }
12473         if (!(item instanceof Roo.Toolbar.Button)){
12474            item = new Roo.Toolbar.Button(item);
12475         }
12476         var td = document.createElement("td");
12477         this.tr.insertBefore(td, this.tr.childNodes[index]);
12478         item.render(td);
12479         this.items.insert(index, item);
12480         return item;
12481     },
12482     
12483     /**
12484      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12485      * @param {Object} config
12486      * @return {Roo.Toolbar.Item} The element's item
12487      */
12488     addDom : function(config, returnEl){
12489         var td = this.nextBlock();
12490         Roo.DomHelper.overwrite(td, config);
12491         var ti = new Roo.Toolbar.Item(td.firstChild);
12492         ti.render(td);
12493         this.items.add(ti);
12494         return ti;
12495     },
12496
12497     /**
12498      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12499      * @type Roo.util.MixedCollection  
12500      */
12501     fields : false,
12502     
12503     /**
12504      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12505      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12506      * @param {Roo.form.Field} field
12507      * @return {Roo.ToolbarItem}
12508      */
12509      
12510       
12511     addField : function(field) {
12512         if (!this.fields) {
12513             var autoId = 0;
12514             this.fields = new Roo.util.MixedCollection(false, function(o){
12515                 return o.id || ("item" + (++autoId));
12516             });
12517
12518         }
12519         
12520         var td = this.nextBlock();
12521         field.render(td);
12522         var ti = new Roo.Toolbar.Item(td.firstChild);
12523         ti.render(td);
12524         this.items.add(ti);
12525         this.fields.add(field);
12526         return ti;
12527     },
12528     /**
12529      * Hide the toolbar
12530      * @method hide
12531      */
12532      
12533       
12534     hide : function()
12535     {
12536         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12537         this.el.child('div').hide();
12538     },
12539     /**
12540      * Show the toolbar
12541      * @method show
12542      */
12543     show : function()
12544     {
12545         this.el.child('div').show();
12546     },
12547       
12548     // private
12549     nextBlock : function(){
12550         var td = document.createElement("td");
12551         this.tr.appendChild(td);
12552         return td;
12553     },
12554
12555     // private
12556     destroy : function(){
12557         if(this.items){ // rendered?
12558             Roo.destroy.apply(Roo, this.items.items);
12559         }
12560         if(this.fields){ // rendered?
12561             Roo.destroy.apply(Roo, this.fields.items);
12562         }
12563         Roo.Element.uncache(this.el, this.tr);
12564     }
12565 };
12566
12567 /**
12568  * @class Roo.Toolbar.Item
12569  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12570  * @constructor
12571  * Creates a new Item
12572  * @param {HTMLElement} el 
12573  */
12574 Roo.Toolbar.Item = function(el){
12575     this.el = Roo.getDom(el);
12576     this.id = Roo.id(this.el);
12577     this.hidden = false;
12578 };
12579
12580 Roo.Toolbar.Item.prototype = {
12581     
12582     /**
12583      * Get this item's HTML Element
12584      * @return {HTMLElement}
12585      */
12586     getEl : function(){
12587        return this.el;  
12588     },
12589
12590     // private
12591     render : function(td){
12592         this.td = td;
12593         td.appendChild(this.el);
12594     },
12595     
12596     /**
12597      * Removes and destroys this item.
12598      */
12599     destroy : function(){
12600         this.td.parentNode.removeChild(this.td);
12601     },
12602     
12603     /**
12604      * Shows this item.
12605      */
12606     show: function(){
12607         this.hidden = false;
12608         this.td.style.display = "";
12609     },
12610     
12611     /**
12612      * Hides this item.
12613      */
12614     hide: function(){
12615         this.hidden = true;
12616         this.td.style.display = "none";
12617     },
12618     
12619     /**
12620      * Convenience function for boolean show/hide.
12621      * @param {Boolean} visible true to show/false to hide
12622      */
12623     setVisible: function(visible){
12624         if(visible) {
12625             this.show();
12626         }else{
12627             this.hide();
12628         }
12629     },
12630     
12631     /**
12632      * Try to focus this item.
12633      */
12634     focus : function(){
12635         Roo.fly(this.el).focus();
12636     },
12637     
12638     /**
12639      * Disables this item.
12640      */
12641     disable : function(){
12642         Roo.fly(this.td).addClass("x-item-disabled");
12643         this.disabled = true;
12644         this.el.disabled = true;
12645     },
12646     
12647     /**
12648      * Enables this item.
12649      */
12650     enable : function(){
12651         Roo.fly(this.td).removeClass("x-item-disabled");
12652         this.disabled = false;
12653         this.el.disabled = false;
12654     }
12655 };
12656
12657
12658 /**
12659  * @class Roo.Toolbar.Separator
12660  * @extends Roo.Toolbar.Item
12661  * A simple toolbar separator class
12662  * @constructor
12663  * Creates a new Separator
12664  */
12665 Roo.Toolbar.Separator = function(){
12666     var s = document.createElement("span");
12667     s.className = "ytb-sep";
12668     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12669 };
12670 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12671     enable:Roo.emptyFn,
12672     disable:Roo.emptyFn,
12673     focus:Roo.emptyFn
12674 });
12675
12676 /**
12677  * @class Roo.Toolbar.Spacer
12678  * @extends Roo.Toolbar.Item
12679  * A simple element that adds extra horizontal space to a toolbar.
12680  * @constructor
12681  * Creates a new Spacer
12682  */
12683 Roo.Toolbar.Spacer = function(){
12684     var s = document.createElement("div");
12685     s.className = "ytb-spacer";
12686     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12687 };
12688 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12689     enable:Roo.emptyFn,
12690     disable:Roo.emptyFn,
12691     focus:Roo.emptyFn
12692 });
12693
12694 /**
12695  * @class Roo.Toolbar.Fill
12696  * @extends Roo.Toolbar.Spacer
12697  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12698  * @constructor
12699  * Creates a new Spacer
12700  */
12701 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12702     // private
12703     render : function(td){
12704         td.style.width = '100%';
12705         Roo.Toolbar.Fill.superclass.render.call(this, td);
12706     }
12707 });
12708
12709 /**
12710  * @class Roo.Toolbar.TextItem
12711  * @extends Roo.Toolbar.Item
12712  * A simple class that renders text directly into a toolbar.
12713  * @constructor
12714  * Creates a new TextItem
12715  * @param {String} text
12716  */
12717 Roo.Toolbar.TextItem = function(text){
12718     if (typeof(text) == 'object') {
12719         text = text.text;
12720     }
12721     var s = document.createElement("span");
12722     s.className = "ytb-text";
12723     s.innerHTML = text;
12724     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12725 };
12726 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12727     enable:Roo.emptyFn,
12728     disable:Roo.emptyFn,
12729     focus:Roo.emptyFn
12730 });
12731
12732 /**
12733  * @class Roo.Toolbar.Button
12734  * @extends Roo.Button
12735  * A button that renders into a toolbar.
12736  * @constructor
12737  * Creates a new Button
12738  * @param {Object} config A standard {@link Roo.Button} config object
12739  */
12740 Roo.Toolbar.Button = function(config){
12741     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12742 };
12743 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12744     render : function(td){
12745         this.td = td;
12746         Roo.Toolbar.Button.superclass.render.call(this, td);
12747     },
12748     
12749     /**
12750      * Removes and destroys this button
12751      */
12752     destroy : function(){
12753         Roo.Toolbar.Button.superclass.destroy.call(this);
12754         this.td.parentNode.removeChild(this.td);
12755     },
12756     
12757     /**
12758      * Shows this button
12759      */
12760     show: function(){
12761         this.hidden = false;
12762         this.td.style.display = "";
12763     },
12764     
12765     /**
12766      * Hides this button
12767      */
12768     hide: function(){
12769         this.hidden = true;
12770         this.td.style.display = "none";
12771     },
12772
12773     /**
12774      * Disables this item
12775      */
12776     disable : function(){
12777         Roo.fly(this.td).addClass("x-item-disabled");
12778         this.disabled = true;
12779     },
12780
12781     /**
12782      * Enables this item
12783      */
12784     enable : function(){
12785         Roo.fly(this.td).removeClass("x-item-disabled");
12786         this.disabled = false;
12787     }
12788 });
12789 // backwards compat
12790 Roo.ToolbarButton = Roo.Toolbar.Button;
12791
12792 /**
12793  * @class Roo.Toolbar.SplitButton
12794  * @extends Roo.SplitButton
12795  * A menu button that renders into a toolbar.
12796  * @constructor
12797  * Creates a new SplitButton
12798  * @param {Object} config A standard {@link Roo.SplitButton} config object
12799  */
12800 Roo.Toolbar.SplitButton = function(config){
12801     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12802 };
12803 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12804     render : function(td){
12805         this.td = td;
12806         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12807     },
12808     
12809     /**
12810      * Removes and destroys this button
12811      */
12812     destroy : function(){
12813         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12814         this.td.parentNode.removeChild(this.td);
12815     },
12816     
12817     /**
12818      * Shows this button
12819      */
12820     show: function(){
12821         this.hidden = false;
12822         this.td.style.display = "";
12823     },
12824     
12825     /**
12826      * Hides this button
12827      */
12828     hide: function(){
12829         this.hidden = true;
12830         this.td.style.display = "none";
12831     }
12832 });
12833
12834 // backwards compat
12835 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12836  * Based on:
12837  * Ext JS Library 1.1.1
12838  * Copyright(c) 2006-2007, Ext JS, LLC.
12839  *
12840  * Originally Released Under LGPL - original licence link has changed is not relivant.
12841  *
12842  * Fork - LGPL
12843  * <script type="text/javascript">
12844  */
12845  
12846 /**
12847  * @class Roo.PagingToolbar
12848  * @extends Roo.Toolbar
12849  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12850  * @constructor
12851  * Create a new PagingToolbar
12852  * @param {Object} config The config object
12853  */
12854 Roo.PagingToolbar = function(el, ds, config)
12855 {
12856     // old args format still supported... - xtype is prefered..
12857     if (typeof(el) == 'object' && el.xtype) {
12858         // created from xtype...
12859         config = el;
12860         ds = el.dataSource;
12861         el = config.container;
12862     }
12863     var items = [];
12864     if (config.items) {
12865         items = config.items;
12866         config.items = [];
12867     }
12868     
12869     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12870     this.ds = ds;
12871     this.cursor = 0;
12872     this.renderButtons(this.el);
12873     this.bind(ds);
12874     
12875     // supprot items array.
12876    
12877     Roo.each(items, function(e) {
12878         this.add(Roo.factory(e));
12879     },this);
12880     
12881 };
12882
12883 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12884     /**
12885      * @cfg {Roo.data.Store} dataSource
12886      * The underlying data store providing the paged data
12887      */
12888     /**
12889      * @cfg {String/HTMLElement/Element} container
12890      * container The id or element that will contain the toolbar
12891      */
12892     /**
12893      * @cfg {Boolean} displayInfo
12894      * True to display the displayMsg (defaults to false)
12895      */
12896     /**
12897      * @cfg {Number} pageSize
12898      * The number of records to display per page (defaults to 20)
12899      */
12900     pageSize: 20,
12901     /**
12902      * @cfg {String} displayMsg
12903      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12904      */
12905     displayMsg : 'Displaying {0} - {1} of {2}',
12906     /**
12907      * @cfg {String} emptyMsg
12908      * The message to display when no records are found (defaults to "No data to display")
12909      */
12910     emptyMsg : 'No data to display',
12911     /**
12912      * Customizable piece of the default paging text (defaults to "Page")
12913      * @type String
12914      */
12915     beforePageText : "Page",
12916     /**
12917      * Customizable piece of the default paging text (defaults to "of %0")
12918      * @type String
12919      */
12920     afterPageText : "of {0}",
12921     /**
12922      * Customizable piece of the default paging text (defaults to "First Page")
12923      * @type String
12924      */
12925     firstText : "First Page",
12926     /**
12927      * Customizable piece of the default paging text (defaults to "Previous Page")
12928      * @type String
12929      */
12930     prevText : "Previous Page",
12931     /**
12932      * Customizable piece of the default paging text (defaults to "Next Page")
12933      * @type String
12934      */
12935     nextText : "Next Page",
12936     /**
12937      * Customizable piece of the default paging text (defaults to "Last Page")
12938      * @type String
12939      */
12940     lastText : "Last Page",
12941     /**
12942      * Customizable piece of the default paging text (defaults to "Refresh")
12943      * @type String
12944      */
12945     refreshText : "Refresh",
12946
12947     // private
12948     renderButtons : function(el){
12949         Roo.PagingToolbar.superclass.render.call(this, el);
12950         this.first = this.addButton({
12951             tooltip: this.firstText,
12952             cls: "x-btn-icon x-grid-page-first",
12953             disabled: true,
12954             handler: this.onClick.createDelegate(this, ["first"])
12955         });
12956         this.prev = this.addButton({
12957             tooltip: this.prevText,
12958             cls: "x-btn-icon x-grid-page-prev",
12959             disabled: true,
12960             handler: this.onClick.createDelegate(this, ["prev"])
12961         });
12962         //this.addSeparator();
12963         this.add(this.beforePageText);
12964         this.field = Roo.get(this.addDom({
12965            tag: "input",
12966            type: "text",
12967            size: "3",
12968            value: "1",
12969            cls: "x-grid-page-number"
12970         }).el);
12971         this.field.on("keydown", this.onPagingKeydown, this);
12972         this.field.on("focus", function(){this.dom.select();});
12973         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12974         this.field.setHeight(18);
12975         //this.addSeparator();
12976         this.next = this.addButton({
12977             tooltip: this.nextText,
12978             cls: "x-btn-icon x-grid-page-next",
12979             disabled: true,
12980             handler: this.onClick.createDelegate(this, ["next"])
12981         });
12982         this.last = this.addButton({
12983             tooltip: this.lastText,
12984             cls: "x-btn-icon x-grid-page-last",
12985             disabled: true,
12986             handler: this.onClick.createDelegate(this, ["last"])
12987         });
12988         //this.addSeparator();
12989         this.loading = this.addButton({
12990             tooltip: this.refreshText,
12991             cls: "x-btn-icon x-grid-loading",
12992             handler: this.onClick.createDelegate(this, ["refresh"])
12993         });
12994
12995         if(this.displayInfo){
12996             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12997         }
12998     },
12999
13000     // private
13001     updateInfo : function(){
13002         if(this.displayEl){
13003             var count = this.ds.getCount();
13004             var msg = count == 0 ?
13005                 this.emptyMsg :
13006                 String.format(
13007                     this.displayMsg,
13008                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13009                 );
13010             this.displayEl.update(msg);
13011         }
13012     },
13013
13014     // private
13015     onLoad : function(ds, r, o){
13016        this.cursor = o.params ? o.params.start : 0;
13017        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13018
13019        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13020        this.field.dom.value = ap;
13021        this.first.setDisabled(ap == 1);
13022        this.prev.setDisabled(ap == 1);
13023        this.next.setDisabled(ap == ps);
13024        this.last.setDisabled(ap == ps);
13025        this.loading.enable();
13026        this.updateInfo();
13027     },
13028
13029     // private
13030     getPageData : function(){
13031         var total = this.ds.getTotalCount();
13032         return {
13033             total : total,
13034             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13035             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13036         };
13037     },
13038
13039     // private
13040     onLoadError : function(){
13041         this.loading.enable();
13042     },
13043
13044     // private
13045     onPagingKeydown : function(e){
13046         var k = e.getKey();
13047         var d = this.getPageData();
13048         if(k == e.RETURN){
13049             var v = this.field.dom.value, pageNum;
13050             if(!v || isNaN(pageNum = parseInt(v, 10))){
13051                 this.field.dom.value = d.activePage;
13052                 return;
13053             }
13054             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13055             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13056             e.stopEvent();
13057         }
13058         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))
13059         {
13060           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13061           this.field.dom.value = pageNum;
13062           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13063           e.stopEvent();
13064         }
13065         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13066         {
13067           var v = this.field.dom.value, pageNum; 
13068           var increment = (e.shiftKey) ? 10 : 1;
13069           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13070             increment *= -1;
13071           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13072             this.field.dom.value = d.activePage;
13073             return;
13074           }
13075           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13076           {
13077             this.field.dom.value = parseInt(v, 10) + increment;
13078             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13079             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13080           }
13081           e.stopEvent();
13082         }
13083     },
13084
13085     // private
13086     beforeLoad : function(){
13087         if(this.loading){
13088             this.loading.disable();
13089         }
13090     },
13091
13092     // private
13093     onClick : function(which){
13094         var ds = this.ds;
13095         switch(which){
13096             case "first":
13097                 ds.load({params:{start: 0, limit: this.pageSize}});
13098             break;
13099             case "prev":
13100                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13101             break;
13102             case "next":
13103                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13104             break;
13105             case "last":
13106                 var total = ds.getTotalCount();
13107                 var extra = total % this.pageSize;
13108                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13109                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13110             break;
13111             case "refresh":
13112                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13113             break;
13114         }
13115     },
13116
13117     /**
13118      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13119      * @param {Roo.data.Store} store The data store to unbind
13120      */
13121     unbind : function(ds){
13122         ds.un("beforeload", this.beforeLoad, this);
13123         ds.un("load", this.onLoad, this);
13124         ds.un("loadexception", this.onLoadError, this);
13125         ds.un("remove", this.updateInfo, this);
13126         ds.un("add", this.updateInfo, this);
13127         this.ds = undefined;
13128     },
13129
13130     /**
13131      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13132      * @param {Roo.data.Store} store The data store to bind
13133      */
13134     bind : function(ds){
13135         ds.on("beforeload", this.beforeLoad, this);
13136         ds.on("load", this.onLoad, this);
13137         ds.on("loadexception", this.onLoadError, this);
13138         ds.on("remove", this.updateInfo, this);
13139         ds.on("add", this.updateInfo, this);
13140         this.ds = ds;
13141     }
13142 });/*
13143  * Based on:
13144  * Ext JS Library 1.1.1
13145  * Copyright(c) 2006-2007, Ext JS, LLC.
13146  *
13147  * Originally Released Under LGPL - original licence link has changed is not relivant.
13148  *
13149  * Fork - LGPL
13150  * <script type="text/javascript">
13151  */
13152
13153 /**
13154  * @class Roo.Resizable
13155  * @extends Roo.util.Observable
13156  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13157  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13158  * 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
13159  * the element will be wrapped for you automatically.</p>
13160  * <p>Here is the list of valid resize handles:</p>
13161  * <pre>
13162 Value   Description
13163 ------  -------------------
13164  'n'     north
13165  's'     south
13166  'e'     east
13167  'w'     west
13168  'nw'    northwest
13169  'sw'    southwest
13170  'se'    southeast
13171  'ne'    northeast
13172  'hd'    horizontal drag
13173  'all'   all
13174 </pre>
13175  * <p>Here's an example showing the creation of a typical Resizable:</p>
13176  * <pre><code>
13177 var resizer = new Roo.Resizable("element-id", {
13178     handles: 'all',
13179     minWidth: 200,
13180     minHeight: 100,
13181     maxWidth: 500,
13182     maxHeight: 400,
13183     pinned: true
13184 });
13185 resizer.on("resize", myHandler);
13186 </code></pre>
13187  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13188  * resizer.east.setDisplayed(false);</p>
13189  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13190  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13191  * resize operation's new size (defaults to [0, 0])
13192  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13193  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13194  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13195  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13196  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13197  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13198  * @cfg {Number} width The width of the element in pixels (defaults to null)
13199  * @cfg {Number} height The height of the element in pixels (defaults to null)
13200  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13201  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13202  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13203  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13204  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13205  * in favor of the handles config option (defaults to false)
13206  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13207  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13208  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13209  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13210  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13211  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13212  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13213  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13214  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13215  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13216  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13217  * @constructor
13218  * Create a new resizable component
13219  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13220  * @param {Object} config configuration options
13221   */
13222 Roo.Resizable = function(el, config)
13223 {
13224     this.el = Roo.get(el);
13225
13226     if(config && config.wrap){
13227         config.resizeChild = this.el;
13228         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13229         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13230         this.el.setStyle("overflow", "hidden");
13231         this.el.setPositioning(config.resizeChild.getPositioning());
13232         config.resizeChild.clearPositioning();
13233         if(!config.width || !config.height){
13234             var csize = config.resizeChild.getSize();
13235             this.el.setSize(csize.width, csize.height);
13236         }
13237         if(config.pinned && !config.adjustments){
13238             config.adjustments = "auto";
13239         }
13240     }
13241
13242     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13243     this.proxy.unselectable();
13244     this.proxy.enableDisplayMode('block');
13245
13246     Roo.apply(this, config);
13247
13248     if(this.pinned){
13249         this.disableTrackOver = true;
13250         this.el.addClass("x-resizable-pinned");
13251     }
13252     // if the element isn't positioned, make it relative
13253     var position = this.el.getStyle("position");
13254     if(position != "absolute" && position != "fixed"){
13255         this.el.setStyle("position", "relative");
13256     }
13257     if(!this.handles){ // no handles passed, must be legacy style
13258         this.handles = 's,e,se';
13259         if(this.multiDirectional){
13260             this.handles += ',n,w';
13261         }
13262     }
13263     if(this.handles == "all"){
13264         this.handles = "n s e w ne nw se sw";
13265     }
13266     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13267     var ps = Roo.Resizable.positions;
13268     for(var i = 0, len = hs.length; i < len; i++){
13269         if(hs[i] && ps[hs[i]]){
13270             var pos = ps[hs[i]];
13271             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13272         }
13273     }
13274     // legacy
13275     this.corner = this.southeast;
13276     
13277     // updateBox = the box can move..
13278     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13279         this.updateBox = true;
13280     }
13281
13282     this.activeHandle = null;
13283
13284     if(this.resizeChild){
13285         if(typeof this.resizeChild == "boolean"){
13286             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13287         }else{
13288             this.resizeChild = Roo.get(this.resizeChild, true);
13289         }
13290     }
13291     
13292     if(this.adjustments == "auto"){
13293         var rc = this.resizeChild;
13294         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13295         if(rc && (hw || hn)){
13296             rc.position("relative");
13297             rc.setLeft(hw ? hw.el.getWidth() : 0);
13298             rc.setTop(hn ? hn.el.getHeight() : 0);
13299         }
13300         this.adjustments = [
13301             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13302             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13303         ];
13304     }
13305
13306     if(this.draggable){
13307         this.dd = this.dynamic ?
13308             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13309         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13310     }
13311
13312     // public events
13313     this.addEvents({
13314         /**
13315          * @event beforeresize
13316          * Fired before resize is allowed. Set enabled to false to cancel resize.
13317          * @param {Roo.Resizable} this
13318          * @param {Roo.EventObject} e The mousedown event
13319          */
13320         "beforeresize" : true,
13321         /**
13322          * @event resize
13323          * Fired after a resize.
13324          * @param {Roo.Resizable} this
13325          * @param {Number} width The new width
13326          * @param {Number} height The new height
13327          * @param {Roo.EventObject} e The mouseup event
13328          */
13329         "resize" : true
13330     });
13331
13332     if(this.width !== null && this.height !== null){
13333         this.resizeTo(this.width, this.height);
13334     }else{
13335         this.updateChildSize();
13336     }
13337     if(Roo.isIE){
13338         this.el.dom.style.zoom = 1;
13339     }
13340     Roo.Resizable.superclass.constructor.call(this);
13341 };
13342
13343 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13344         resizeChild : false,
13345         adjustments : [0, 0],
13346         minWidth : 5,
13347         minHeight : 5,
13348         maxWidth : 10000,
13349         maxHeight : 10000,
13350         enabled : true,
13351         animate : false,
13352         duration : .35,
13353         dynamic : false,
13354         handles : false,
13355         multiDirectional : false,
13356         disableTrackOver : false,
13357         easing : 'easeOutStrong',
13358         widthIncrement : 0,
13359         heightIncrement : 0,
13360         pinned : false,
13361         width : null,
13362         height : null,
13363         preserveRatio : false,
13364         transparent: false,
13365         minX: 0,
13366         minY: 0,
13367         draggable: false,
13368
13369         /**
13370          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13371          */
13372         constrainTo: undefined,
13373         /**
13374          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13375          */
13376         resizeRegion: undefined,
13377
13378
13379     /**
13380      * Perform a manual resize
13381      * @param {Number} width
13382      * @param {Number} height
13383      */
13384     resizeTo : function(width, height){
13385         this.el.setSize(width, height);
13386         this.updateChildSize();
13387         this.fireEvent("resize", this, width, height, null);
13388     },
13389
13390     // private
13391     startSizing : function(e, handle){
13392         this.fireEvent("beforeresize", this, e);
13393         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13394
13395             if(!this.overlay){
13396                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13397                 this.overlay.unselectable();
13398                 this.overlay.enableDisplayMode("block");
13399                 this.overlay.on("mousemove", this.onMouseMove, this);
13400                 this.overlay.on("mouseup", this.onMouseUp, this);
13401             }
13402             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13403
13404             this.resizing = true;
13405             this.startBox = this.el.getBox();
13406             this.startPoint = e.getXY();
13407             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13408                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13409
13410             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13411             this.overlay.show();
13412
13413             if(this.constrainTo) {
13414                 var ct = Roo.get(this.constrainTo);
13415                 this.resizeRegion = ct.getRegion().adjust(
13416                     ct.getFrameWidth('t'),
13417                     ct.getFrameWidth('l'),
13418                     -ct.getFrameWidth('b'),
13419                     -ct.getFrameWidth('r')
13420                 );
13421             }
13422
13423             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13424             this.proxy.show();
13425             this.proxy.setBox(this.startBox);
13426             if(!this.dynamic){
13427                 this.proxy.setStyle('visibility', 'visible');
13428             }
13429         }
13430     },
13431
13432     // private
13433     onMouseDown : function(handle, e){
13434         if(this.enabled){
13435             e.stopEvent();
13436             this.activeHandle = handle;
13437             this.startSizing(e, handle);
13438         }
13439     },
13440
13441     // private
13442     onMouseUp : function(e){
13443         var size = this.resizeElement();
13444         this.resizing = false;
13445         this.handleOut();
13446         this.overlay.hide();
13447         this.proxy.hide();
13448         this.fireEvent("resize", this, size.width, size.height, e);
13449     },
13450
13451     // private
13452     updateChildSize : function(){
13453         if(this.resizeChild){
13454             var el = this.el;
13455             var child = this.resizeChild;
13456             var adj = this.adjustments;
13457             if(el.dom.offsetWidth){
13458                 var b = el.getSize(true);
13459                 child.setSize(b.width+adj[0], b.height+adj[1]);
13460             }
13461             // Second call here for IE
13462             // The first call enables instant resizing and
13463             // the second call corrects scroll bars if they
13464             // exist
13465             if(Roo.isIE){
13466                 setTimeout(function(){
13467                     if(el.dom.offsetWidth){
13468                         var b = el.getSize(true);
13469                         child.setSize(b.width+adj[0], b.height+adj[1]);
13470                     }
13471                 }, 10);
13472             }
13473         }
13474     },
13475
13476     // private
13477     snap : function(value, inc, min){
13478         if(!inc || !value) return value;
13479         var newValue = value;
13480         var m = value % inc;
13481         if(m > 0){
13482             if(m > (inc/2)){
13483                 newValue = value + (inc-m);
13484             }else{
13485                 newValue = value - m;
13486             }
13487         }
13488         return Math.max(min, newValue);
13489     },
13490
13491     // private
13492     resizeElement : function(){
13493         var box = this.proxy.getBox();
13494         if(this.updateBox){
13495             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13496         }else{
13497             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13498         }
13499         this.updateChildSize();
13500         if(!this.dynamic){
13501             this.proxy.hide();
13502         }
13503         return box;
13504     },
13505
13506     // private
13507     constrain : function(v, diff, m, mx){
13508         if(v - diff < m){
13509             diff = v - m;
13510         }else if(v - diff > mx){
13511             diff = mx - v;
13512         }
13513         return diff;
13514     },
13515
13516     // private
13517     onMouseMove : function(e){
13518         if(this.enabled){
13519             try{// try catch so if something goes wrong the user doesn't get hung
13520
13521             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13522                 return;
13523             }
13524
13525             //var curXY = this.startPoint;
13526             var curSize = this.curSize || this.startBox;
13527             var x = this.startBox.x, y = this.startBox.y;
13528             var ox = x, oy = y;
13529             var w = curSize.width, h = curSize.height;
13530             var ow = w, oh = h;
13531             var mw = this.minWidth, mh = this.minHeight;
13532             var mxw = this.maxWidth, mxh = this.maxHeight;
13533             var wi = this.widthIncrement;
13534             var hi = this.heightIncrement;
13535
13536             var eventXY = e.getXY();
13537             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13538             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13539
13540             var pos = this.activeHandle.position;
13541
13542             switch(pos){
13543                 case "east":
13544                     w += diffX;
13545                     w = Math.min(Math.max(mw, w), mxw);
13546                     break;
13547              
13548                 case "south":
13549                     h += diffY;
13550                     h = Math.min(Math.max(mh, h), mxh);
13551                     break;
13552                 case "southeast":
13553                     w += diffX;
13554                     h += diffY;
13555                     w = Math.min(Math.max(mw, w), mxw);
13556                     h = Math.min(Math.max(mh, h), mxh);
13557                     break;
13558                 case "north":
13559                     diffY = this.constrain(h, diffY, mh, mxh);
13560                     y += diffY;
13561                     h -= diffY;
13562                     break;
13563                 case "hdrag":
13564                     
13565                     if (wi) {
13566                         var adiffX = Math.abs(diffX);
13567                         var sub = (adiffX % wi); // how much 
13568                         if (sub > (wi/2)) { // far enough to snap
13569                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13570                         } else {
13571                             // remove difference.. 
13572                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13573                         }
13574                     }
13575                     x += diffX;
13576                     x = Math.max(this.minX, x);
13577                     break;
13578                 case "west":
13579                     diffX = this.constrain(w, diffX, mw, mxw);
13580                     x += diffX;
13581                     w -= diffX;
13582                     break;
13583                 case "northeast":
13584                     w += diffX;
13585                     w = Math.min(Math.max(mw, w), mxw);
13586                     diffY = this.constrain(h, diffY, mh, mxh);
13587                     y += diffY;
13588                     h -= diffY;
13589                     break;
13590                 case "northwest":
13591                     diffX = this.constrain(w, diffX, mw, mxw);
13592                     diffY = this.constrain(h, diffY, mh, mxh);
13593                     y += diffY;
13594                     h -= diffY;
13595                     x += diffX;
13596                     w -= diffX;
13597                     break;
13598                case "southwest":
13599                     diffX = this.constrain(w, diffX, mw, mxw);
13600                     h += diffY;
13601                     h = Math.min(Math.max(mh, h), mxh);
13602                     x += diffX;
13603                     w -= diffX;
13604                     break;
13605             }
13606
13607             var sw = this.snap(w, wi, mw);
13608             var sh = this.snap(h, hi, mh);
13609             if(sw != w || sh != h){
13610                 switch(pos){
13611                     case "northeast":
13612                         y -= sh - h;
13613                     break;
13614                     case "north":
13615                         y -= sh - h;
13616                         break;
13617                     case "southwest":
13618                         x -= sw - w;
13619                     break;
13620                     case "west":
13621                         x -= sw - w;
13622                         break;
13623                     case "northwest":
13624                         x -= sw - w;
13625                         y -= sh - h;
13626                     break;
13627                 }
13628                 w = sw;
13629                 h = sh;
13630             }
13631
13632             if(this.preserveRatio){
13633                 switch(pos){
13634                     case "southeast":
13635                     case "east":
13636                         h = oh * (w/ow);
13637                         h = Math.min(Math.max(mh, h), mxh);
13638                         w = ow * (h/oh);
13639                        break;
13640                     case "south":
13641                         w = ow * (h/oh);
13642                         w = Math.min(Math.max(mw, w), mxw);
13643                         h = oh * (w/ow);
13644                         break;
13645                     case "northeast":
13646                         w = ow * (h/oh);
13647                         w = Math.min(Math.max(mw, w), mxw);
13648                         h = oh * (w/ow);
13649                     break;
13650                     case "north":
13651                         var tw = w;
13652                         w = ow * (h/oh);
13653                         w = Math.min(Math.max(mw, w), mxw);
13654                         h = oh * (w/ow);
13655                         x += (tw - w) / 2;
13656                         break;
13657                     case "southwest":
13658                         h = oh * (w/ow);
13659                         h = Math.min(Math.max(mh, h), mxh);
13660                         var tw = w;
13661                         w = ow * (h/oh);
13662                         x += tw - w;
13663                         break;
13664                     case "west":
13665                         var th = h;
13666                         h = oh * (w/ow);
13667                         h = Math.min(Math.max(mh, h), mxh);
13668                         y += (th - h) / 2;
13669                         var tw = w;
13670                         w = ow * (h/oh);
13671                         x += tw - w;
13672                        break;
13673                     case "northwest":
13674                         var tw = w;
13675                         var th = h;
13676                         h = oh * (w/ow);
13677                         h = Math.min(Math.max(mh, h), mxh);
13678                         w = ow * (h/oh);
13679                         y += th - h;
13680                         x += tw - w;
13681                        break;
13682
13683                 }
13684             }
13685             if (pos == 'hdrag') {
13686                 w = ow;
13687             }
13688             this.proxy.setBounds(x, y, w, h);
13689             if(this.dynamic){
13690                 this.resizeElement();
13691             }
13692             }catch(e){}
13693         }
13694     },
13695
13696     // private
13697     handleOver : function(){
13698         if(this.enabled){
13699             this.el.addClass("x-resizable-over");
13700         }
13701     },
13702
13703     // private
13704     handleOut : function(){
13705         if(!this.resizing){
13706             this.el.removeClass("x-resizable-over");
13707         }
13708     },
13709
13710     /**
13711      * Returns the element this component is bound to.
13712      * @return {Roo.Element}
13713      */
13714     getEl : function(){
13715         return this.el;
13716     },
13717
13718     /**
13719      * Returns the resizeChild element (or null).
13720      * @return {Roo.Element}
13721      */
13722     getResizeChild : function(){
13723         return this.resizeChild;
13724     },
13725
13726     /**
13727      * Destroys this resizable. If the element was wrapped and
13728      * removeEl is not true then the element remains.
13729      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13730      */
13731     destroy : function(removeEl){
13732         this.proxy.remove();
13733         if(this.overlay){
13734             this.overlay.removeAllListeners();
13735             this.overlay.remove();
13736         }
13737         var ps = Roo.Resizable.positions;
13738         for(var k in ps){
13739             if(typeof ps[k] != "function" && this[ps[k]]){
13740                 var h = this[ps[k]];
13741                 h.el.removeAllListeners();
13742                 h.el.remove();
13743             }
13744         }
13745         if(removeEl){
13746             this.el.update("");
13747             this.el.remove();
13748         }
13749     }
13750 });
13751
13752 // private
13753 // hash to map config positions to true positions
13754 Roo.Resizable.positions = {
13755     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13756     hd: "hdrag"
13757 };
13758
13759 // private
13760 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13761     if(!this.tpl){
13762         // only initialize the template if resizable is used
13763         var tpl = Roo.DomHelper.createTemplate(
13764             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13765         );
13766         tpl.compile();
13767         Roo.Resizable.Handle.prototype.tpl = tpl;
13768     }
13769     this.position = pos;
13770     this.rz = rz;
13771     // show north drag fro topdra
13772     var handlepos = pos == 'hdrag' ? 'north' : pos;
13773     
13774     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13775     if (pos == 'hdrag') {
13776         this.el.setStyle('cursor', 'pointer');
13777     }
13778     this.el.unselectable();
13779     if(transparent){
13780         this.el.setOpacity(0);
13781     }
13782     this.el.on("mousedown", this.onMouseDown, this);
13783     if(!disableTrackOver){
13784         this.el.on("mouseover", this.onMouseOver, this);
13785         this.el.on("mouseout", this.onMouseOut, this);
13786     }
13787 };
13788
13789 // private
13790 Roo.Resizable.Handle.prototype = {
13791     afterResize : function(rz){
13792         // do nothing
13793     },
13794     // private
13795     onMouseDown : function(e){
13796         this.rz.onMouseDown(this, e);
13797     },
13798     // private
13799     onMouseOver : function(e){
13800         this.rz.handleOver(this, e);
13801     },
13802     // private
13803     onMouseOut : function(e){
13804         this.rz.handleOut(this, e);
13805     }
13806 };/*
13807  * Based on:
13808  * Ext JS Library 1.1.1
13809  * Copyright(c) 2006-2007, Ext JS, LLC.
13810  *
13811  * Originally Released Under LGPL - original licence link has changed is not relivant.
13812  *
13813  * Fork - LGPL
13814  * <script type="text/javascript">
13815  */
13816
13817 /**
13818  * @class Roo.Editor
13819  * @extends Roo.Component
13820  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13821  * @constructor
13822  * Create a new Editor
13823  * @param {Roo.form.Field} field The Field object (or descendant)
13824  * @param {Object} config The config object
13825  */
13826 Roo.Editor = function(field, config){
13827     Roo.Editor.superclass.constructor.call(this, config);
13828     this.field = field;
13829     this.addEvents({
13830         /**
13831              * @event beforestartedit
13832              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13833              * false from the handler of this event.
13834              * @param {Editor} this
13835              * @param {Roo.Element} boundEl The underlying element bound to this editor
13836              * @param {Mixed} value The field value being set
13837              */
13838         "beforestartedit" : true,
13839         /**
13840              * @event startedit
13841              * Fires when this editor is displayed
13842              * @param {Roo.Element} boundEl The underlying element bound to this editor
13843              * @param {Mixed} value The starting field value
13844              */
13845         "startedit" : true,
13846         /**
13847              * @event beforecomplete
13848              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13849              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13850              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13851              * event will not fire since no edit actually occurred.
13852              * @param {Editor} this
13853              * @param {Mixed} value The current field value
13854              * @param {Mixed} startValue The original field value
13855              */
13856         "beforecomplete" : true,
13857         /**
13858              * @event complete
13859              * Fires after editing is complete and any changed value has been written to the underlying field.
13860              * @param {Editor} this
13861              * @param {Mixed} value The current field value
13862              * @param {Mixed} startValue The original field value
13863              */
13864         "complete" : true,
13865         /**
13866          * @event specialkey
13867          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13868          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13869          * @param {Roo.form.Field} this
13870          * @param {Roo.EventObject} e The event object
13871          */
13872         "specialkey" : true
13873     });
13874 };
13875
13876 Roo.extend(Roo.Editor, Roo.Component, {
13877     /**
13878      * @cfg {Boolean/String} autosize
13879      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13880      * or "height" to adopt the height only (defaults to false)
13881      */
13882     /**
13883      * @cfg {Boolean} revertInvalid
13884      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13885      * validation fails (defaults to true)
13886      */
13887     /**
13888      * @cfg {Boolean} ignoreNoChange
13889      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13890      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13891      * will never be ignored.
13892      */
13893     /**
13894      * @cfg {Boolean} hideEl
13895      * False to keep the bound element visible while the editor is displayed (defaults to true)
13896      */
13897     /**
13898      * @cfg {Mixed} value
13899      * The data value of the underlying field (defaults to "")
13900      */
13901     value : "",
13902     /**
13903      * @cfg {String} alignment
13904      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13905      */
13906     alignment: "c-c?",
13907     /**
13908      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13909      * for bottom-right shadow (defaults to "frame")
13910      */
13911     shadow : "frame",
13912     /**
13913      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13914      */
13915     constrain : false,
13916     /**
13917      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13918      */
13919     completeOnEnter : false,
13920     /**
13921      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13922      */
13923     cancelOnEsc : false,
13924     /**
13925      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13926      */
13927     updateEl : false,
13928
13929     // private
13930     onRender : function(ct, position){
13931         this.el = new Roo.Layer({
13932             shadow: this.shadow,
13933             cls: "x-editor",
13934             parentEl : ct,
13935             shim : this.shim,
13936             shadowOffset:4,
13937             id: this.id,
13938             constrain: this.constrain
13939         });
13940         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13941         if(this.field.msgTarget != 'title'){
13942             this.field.msgTarget = 'qtip';
13943         }
13944         this.field.render(this.el);
13945         if(Roo.isGecko){
13946             this.field.el.dom.setAttribute('autocomplete', 'off');
13947         }
13948         this.field.on("specialkey", this.onSpecialKey, this);
13949         if(this.swallowKeys){
13950             this.field.el.swallowEvent(['keydown','keypress']);
13951         }
13952         this.field.show();
13953         this.field.on("blur", this.onBlur, this);
13954         if(this.field.grow){
13955             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13956         }
13957     },
13958
13959     onSpecialKey : function(field, e)
13960     {
13961         //Roo.log('editor onSpecialKey');
13962         if(this.completeOnEnter && e.getKey() == e.ENTER){
13963             e.stopEvent();
13964             this.completeEdit();
13965             return;
13966         }
13967         // do not fire special key otherwise it might hide close the editor...
13968         if(e.getKey() == e.ENTER){    
13969             return;
13970         }
13971         if(this.cancelOnEsc && e.getKey() == e.ESC){
13972             this.cancelEdit();
13973             return;
13974         } 
13975         this.fireEvent('specialkey', field, e);
13976     
13977     },
13978
13979     /**
13980      * Starts the editing process and shows the editor.
13981      * @param {String/HTMLElement/Element} el The element to edit
13982      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13983       * to the innerHTML of el.
13984      */
13985     startEdit : function(el, value){
13986         if(this.editing){
13987             this.completeEdit();
13988         }
13989         this.boundEl = Roo.get(el);
13990         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13991         if(!this.rendered){
13992             this.render(this.parentEl || document.body);
13993         }
13994         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13995             return;
13996         }
13997         this.startValue = v;
13998         this.field.setValue(v);
13999         if(this.autoSize){
14000             var sz = this.boundEl.getSize();
14001             switch(this.autoSize){
14002                 case "width":
14003                 this.setSize(sz.width,  "");
14004                 break;
14005                 case "height":
14006                 this.setSize("",  sz.height);
14007                 break;
14008                 default:
14009                 this.setSize(sz.width,  sz.height);
14010             }
14011         }
14012         this.el.alignTo(this.boundEl, this.alignment);
14013         this.editing = true;
14014         if(Roo.QuickTips){
14015             Roo.QuickTips.disable();
14016         }
14017         this.show();
14018     },
14019
14020     /**
14021      * Sets the height and width of this editor.
14022      * @param {Number} width The new width
14023      * @param {Number} height The new height
14024      */
14025     setSize : function(w, h){
14026         this.field.setSize(w, h);
14027         if(this.el){
14028             this.el.sync();
14029         }
14030     },
14031
14032     /**
14033      * Realigns the editor to the bound field based on the current alignment config value.
14034      */
14035     realign : function(){
14036         this.el.alignTo(this.boundEl, this.alignment);
14037     },
14038
14039     /**
14040      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14041      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14042      */
14043     completeEdit : function(remainVisible){
14044         if(!this.editing){
14045             return;
14046         }
14047         var v = this.getValue();
14048         if(this.revertInvalid !== false && !this.field.isValid()){
14049             v = this.startValue;
14050             this.cancelEdit(true);
14051         }
14052         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14053             this.editing = false;
14054             this.hide();
14055             return;
14056         }
14057         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14058             this.editing = false;
14059             if(this.updateEl && this.boundEl){
14060                 this.boundEl.update(v);
14061             }
14062             if(remainVisible !== true){
14063                 this.hide();
14064             }
14065             this.fireEvent("complete", this, v, this.startValue);
14066         }
14067     },
14068
14069     // private
14070     onShow : function(){
14071         this.el.show();
14072         if(this.hideEl !== false){
14073             this.boundEl.hide();
14074         }
14075         this.field.show();
14076         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14077             this.fixIEFocus = true;
14078             this.deferredFocus.defer(50, this);
14079         }else{
14080             this.field.focus();
14081         }
14082         this.fireEvent("startedit", this.boundEl, this.startValue);
14083     },
14084
14085     deferredFocus : function(){
14086         if(this.editing){
14087             this.field.focus();
14088         }
14089     },
14090
14091     /**
14092      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14093      * reverted to the original starting value.
14094      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14095      * cancel (defaults to false)
14096      */
14097     cancelEdit : function(remainVisible){
14098         if(this.editing){
14099             this.setValue(this.startValue);
14100             if(remainVisible !== true){
14101                 this.hide();
14102             }
14103         }
14104     },
14105
14106     // private
14107     onBlur : function(){
14108         if(this.allowBlur !== true && this.editing){
14109             this.completeEdit();
14110         }
14111     },
14112
14113     // private
14114     onHide : function(){
14115         if(this.editing){
14116             this.completeEdit();
14117             return;
14118         }
14119         this.field.blur();
14120         if(this.field.collapse){
14121             this.field.collapse();
14122         }
14123         this.el.hide();
14124         if(this.hideEl !== false){
14125             this.boundEl.show();
14126         }
14127         if(Roo.QuickTips){
14128             Roo.QuickTips.enable();
14129         }
14130     },
14131
14132     /**
14133      * Sets the data value of the editor
14134      * @param {Mixed} value Any valid value supported by the underlying field
14135      */
14136     setValue : function(v){
14137         this.field.setValue(v);
14138     },
14139
14140     /**
14141      * Gets the data value of the editor
14142      * @return {Mixed} The data value
14143      */
14144     getValue : function(){
14145         return this.field.getValue();
14146     }
14147 });/*
14148  * Based on:
14149  * Ext JS Library 1.1.1
14150  * Copyright(c) 2006-2007, Ext JS, LLC.
14151  *
14152  * Originally Released Under LGPL - original licence link has changed is not relivant.
14153  *
14154  * Fork - LGPL
14155  * <script type="text/javascript">
14156  */
14157  
14158 /**
14159  * @class Roo.BasicDialog
14160  * @extends Roo.util.Observable
14161  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14162  * <pre><code>
14163 var dlg = new Roo.BasicDialog("my-dlg", {
14164     height: 200,
14165     width: 300,
14166     minHeight: 100,
14167     minWidth: 150,
14168     modal: true,
14169     proxyDrag: true,
14170     shadow: true
14171 });
14172 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14173 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14174 dlg.addButton('Cancel', dlg.hide, dlg);
14175 dlg.show();
14176 </code></pre>
14177   <b>A Dialog should always be a direct child of the body element.</b>
14178  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14179  * @cfg {String} title Default text to display in the title bar (defaults to null)
14180  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14181  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14182  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14183  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14184  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14185  * (defaults to null with no animation)
14186  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14187  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14188  * property for valid values (defaults to 'all')
14189  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14190  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14191  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14192  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14193  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14194  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14195  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14196  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14197  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14198  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14199  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14200  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14201  * draggable = true (defaults to false)
14202  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14203  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14204  * shadow (defaults to false)
14205  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14206  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14207  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14208  * @cfg {Array} buttons Array of buttons
14209  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14210  * @constructor
14211  * Create a new BasicDialog.
14212  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14213  * @param {Object} config Configuration options
14214  */
14215 Roo.BasicDialog = function(el, config){
14216     this.el = Roo.get(el);
14217     var dh = Roo.DomHelper;
14218     if(!this.el && config && config.autoCreate){
14219         if(typeof config.autoCreate == "object"){
14220             if(!config.autoCreate.id){
14221                 config.autoCreate.id = el;
14222             }
14223             this.el = dh.append(document.body,
14224                         config.autoCreate, true);
14225         }else{
14226             this.el = dh.append(document.body,
14227                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14228         }
14229     }
14230     el = this.el;
14231     el.setDisplayed(true);
14232     el.hide = this.hideAction;
14233     this.id = el.id;
14234     el.addClass("x-dlg");
14235
14236     Roo.apply(this, config);
14237
14238     this.proxy = el.createProxy("x-dlg-proxy");
14239     this.proxy.hide = this.hideAction;
14240     this.proxy.setOpacity(.5);
14241     this.proxy.hide();
14242
14243     if(config.width){
14244         el.setWidth(config.width);
14245     }
14246     if(config.height){
14247         el.setHeight(config.height);
14248     }
14249     this.size = el.getSize();
14250     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14251         this.xy = [config.x,config.y];
14252     }else{
14253         this.xy = el.getCenterXY(true);
14254     }
14255     /** The header element @type Roo.Element */
14256     this.header = el.child("> .x-dlg-hd");
14257     /** The body element @type Roo.Element */
14258     this.body = el.child("> .x-dlg-bd");
14259     /** The footer element @type Roo.Element */
14260     this.footer = el.child("> .x-dlg-ft");
14261
14262     if(!this.header){
14263         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14264     }
14265     if(!this.body){
14266         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14267     }
14268
14269     this.header.unselectable();
14270     if(this.title){
14271         this.header.update(this.title);
14272     }
14273     // this element allows the dialog to be focused for keyboard event
14274     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14275     this.focusEl.swallowEvent("click", true);
14276
14277     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14278
14279     // wrap the body and footer for special rendering
14280     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14281     if(this.footer){
14282         this.bwrap.dom.appendChild(this.footer.dom);
14283     }
14284
14285     this.bg = this.el.createChild({
14286         tag: "div", cls:"x-dlg-bg",
14287         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14288     });
14289     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14290
14291
14292     if(this.autoScroll !== false && !this.autoTabs){
14293         this.body.setStyle("overflow", "auto");
14294     }
14295
14296     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14297
14298     if(this.closable !== false){
14299         this.el.addClass("x-dlg-closable");
14300         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14301         this.close.on("click", this.closeClick, this);
14302         this.close.addClassOnOver("x-dlg-close-over");
14303     }
14304     if(this.collapsible !== false){
14305         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14306         this.collapseBtn.on("click", this.collapseClick, this);
14307         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14308         this.header.on("dblclick", this.collapseClick, this);
14309     }
14310     if(this.resizable !== false){
14311         this.el.addClass("x-dlg-resizable");
14312         this.resizer = new Roo.Resizable(el, {
14313             minWidth: this.minWidth || 80,
14314             minHeight:this.minHeight || 80,
14315             handles: this.resizeHandles || "all",
14316             pinned: true
14317         });
14318         this.resizer.on("beforeresize", this.beforeResize, this);
14319         this.resizer.on("resize", this.onResize, this);
14320     }
14321     if(this.draggable !== false){
14322         el.addClass("x-dlg-draggable");
14323         if (!this.proxyDrag) {
14324             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14325         }
14326         else {
14327             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14328         }
14329         dd.setHandleElId(this.header.id);
14330         dd.endDrag = this.endMove.createDelegate(this);
14331         dd.startDrag = this.startMove.createDelegate(this);
14332         dd.onDrag = this.onDrag.createDelegate(this);
14333         dd.scroll = false;
14334         this.dd = dd;
14335     }
14336     if(this.modal){
14337         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14338         this.mask.enableDisplayMode("block");
14339         this.mask.hide();
14340         this.el.addClass("x-dlg-modal");
14341     }
14342     if(this.shadow){
14343         this.shadow = new Roo.Shadow({
14344             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14345             offset : this.shadowOffset
14346         });
14347     }else{
14348         this.shadowOffset = 0;
14349     }
14350     if(Roo.useShims && this.shim !== false){
14351         this.shim = this.el.createShim();
14352         this.shim.hide = this.hideAction;
14353         this.shim.hide();
14354     }else{
14355         this.shim = false;
14356     }
14357     if(this.autoTabs){
14358         this.initTabs();
14359     }
14360     if (this.buttons) { 
14361         var bts= this.buttons;
14362         this.buttons = [];
14363         Roo.each(bts, function(b) {
14364             this.addButton(b);
14365         }, this);
14366     }
14367     
14368     
14369     this.addEvents({
14370         /**
14371          * @event keydown
14372          * Fires when a key is pressed
14373          * @param {Roo.BasicDialog} this
14374          * @param {Roo.EventObject} e
14375          */
14376         "keydown" : true,
14377         /**
14378          * @event move
14379          * Fires when this dialog is moved by the user.
14380          * @param {Roo.BasicDialog} this
14381          * @param {Number} x The new page X
14382          * @param {Number} y The new page Y
14383          */
14384         "move" : true,
14385         /**
14386          * @event resize
14387          * Fires when this dialog is resized by the user.
14388          * @param {Roo.BasicDialog} this
14389          * @param {Number} width The new width
14390          * @param {Number} height The new height
14391          */
14392         "resize" : true,
14393         /**
14394          * @event beforehide
14395          * Fires before this dialog is hidden.
14396          * @param {Roo.BasicDialog} this
14397          */
14398         "beforehide" : true,
14399         /**
14400          * @event hide
14401          * Fires when this dialog is hidden.
14402          * @param {Roo.BasicDialog} this
14403          */
14404         "hide" : true,
14405         /**
14406          * @event beforeshow
14407          * Fires before this dialog is shown.
14408          * @param {Roo.BasicDialog} this
14409          */
14410         "beforeshow" : true,
14411         /**
14412          * @event show
14413          * Fires when this dialog is shown.
14414          * @param {Roo.BasicDialog} this
14415          */
14416         "show" : true
14417     });
14418     el.on("keydown", this.onKeyDown, this);
14419     el.on("mousedown", this.toFront, this);
14420     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14421     this.el.hide();
14422     Roo.DialogManager.register(this);
14423     Roo.BasicDialog.superclass.constructor.call(this);
14424 };
14425
14426 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14427     shadowOffset: Roo.isIE ? 6 : 5,
14428     minHeight: 80,
14429     minWidth: 200,
14430     minButtonWidth: 75,
14431     defaultButton: null,
14432     buttonAlign: "right",
14433     tabTag: 'div',
14434     firstShow: true,
14435
14436     /**
14437      * Sets the dialog title text
14438      * @param {String} text The title text to display
14439      * @return {Roo.BasicDialog} this
14440      */
14441     setTitle : function(text){
14442         this.header.update(text);
14443         return this;
14444     },
14445
14446     // private
14447     closeClick : function(){
14448         this.hide();
14449     },
14450
14451     // private
14452     collapseClick : function(){
14453         this[this.collapsed ? "expand" : "collapse"]();
14454     },
14455
14456     /**
14457      * Collapses the dialog to its minimized state (only the title bar is visible).
14458      * Equivalent to the user clicking the collapse dialog button.
14459      */
14460     collapse : function(){
14461         if(!this.collapsed){
14462             this.collapsed = true;
14463             this.el.addClass("x-dlg-collapsed");
14464             this.restoreHeight = this.el.getHeight();
14465             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14466         }
14467     },
14468
14469     /**
14470      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14471      * clicking the expand dialog button.
14472      */
14473     expand : function(){
14474         if(this.collapsed){
14475             this.collapsed = false;
14476             this.el.removeClass("x-dlg-collapsed");
14477             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14478         }
14479     },
14480
14481     /**
14482      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14483      * @return {Roo.TabPanel} The tabs component
14484      */
14485     initTabs : function(){
14486         var tabs = this.getTabs();
14487         while(tabs.getTab(0)){
14488             tabs.removeTab(0);
14489         }
14490         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14491             var dom = el.dom;
14492             tabs.addTab(Roo.id(dom), dom.title);
14493             dom.title = "";
14494         });
14495         tabs.activate(0);
14496         return tabs;
14497     },
14498
14499     // private
14500     beforeResize : function(){
14501         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14502     },
14503
14504     // private
14505     onResize : function(){
14506         this.refreshSize();
14507         this.syncBodyHeight();
14508         this.adjustAssets();
14509         this.focus();
14510         this.fireEvent("resize", this, this.size.width, this.size.height);
14511     },
14512
14513     // private
14514     onKeyDown : function(e){
14515         if(this.isVisible()){
14516             this.fireEvent("keydown", this, e);
14517         }
14518     },
14519
14520     /**
14521      * Resizes the dialog.
14522      * @param {Number} width
14523      * @param {Number} height
14524      * @return {Roo.BasicDialog} this
14525      */
14526     resizeTo : function(width, height){
14527         this.el.setSize(width, height);
14528         this.size = {width: width, height: height};
14529         this.syncBodyHeight();
14530         if(this.fixedcenter){
14531             this.center();
14532         }
14533         if(this.isVisible()){
14534             this.constrainXY();
14535             this.adjustAssets();
14536         }
14537         this.fireEvent("resize", this, width, height);
14538         return this;
14539     },
14540
14541
14542     /**
14543      * Resizes the dialog to fit the specified content size.
14544      * @param {Number} width
14545      * @param {Number} height
14546      * @return {Roo.BasicDialog} this
14547      */
14548     setContentSize : function(w, h){
14549         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14550         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14551         //if(!this.el.isBorderBox()){
14552             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14553             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14554         //}
14555         if(this.tabs){
14556             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14557             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14558         }
14559         this.resizeTo(w, h);
14560         return this;
14561     },
14562
14563     /**
14564      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14565      * executed in response to a particular key being pressed while the dialog is active.
14566      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14567      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14568      * @param {Function} fn The function to call
14569      * @param {Object} scope (optional) The scope of the function
14570      * @return {Roo.BasicDialog} this
14571      */
14572     addKeyListener : function(key, fn, scope){
14573         var keyCode, shift, ctrl, alt;
14574         if(typeof key == "object" && !(key instanceof Array)){
14575             keyCode = key["key"];
14576             shift = key["shift"];
14577             ctrl = key["ctrl"];
14578             alt = key["alt"];
14579         }else{
14580             keyCode = key;
14581         }
14582         var handler = function(dlg, e){
14583             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14584                 var k = e.getKey();
14585                 if(keyCode instanceof Array){
14586                     for(var i = 0, len = keyCode.length; i < len; i++){
14587                         if(keyCode[i] == k){
14588                           fn.call(scope || window, dlg, k, e);
14589                           return;
14590                         }
14591                     }
14592                 }else{
14593                     if(k == keyCode){
14594                         fn.call(scope || window, dlg, k, e);
14595                     }
14596                 }
14597             }
14598         };
14599         this.on("keydown", handler);
14600         return this;
14601     },
14602
14603     /**
14604      * Returns the TabPanel component (creates it if it doesn't exist).
14605      * Note: If you wish to simply check for the existence of tabs without creating them,
14606      * check for a null 'tabs' property.
14607      * @return {Roo.TabPanel} The tabs component
14608      */
14609     getTabs : function(){
14610         if(!this.tabs){
14611             this.el.addClass("x-dlg-auto-tabs");
14612             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14613             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14614         }
14615         return this.tabs;
14616     },
14617
14618     /**
14619      * Adds a button to the footer section of the dialog.
14620      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14621      * object or a valid Roo.DomHelper element config
14622      * @param {Function} handler The function called when the button is clicked
14623      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14624      * @return {Roo.Button} The new button
14625      */
14626     addButton : function(config, handler, scope){
14627         var dh = Roo.DomHelper;
14628         if(!this.footer){
14629             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14630         }
14631         if(!this.btnContainer){
14632             var tb = this.footer.createChild({
14633
14634                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14635                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14636             }, null, true);
14637             this.btnContainer = tb.firstChild.firstChild.firstChild;
14638         }
14639         var bconfig = {
14640             handler: handler,
14641             scope: scope,
14642             minWidth: this.minButtonWidth,
14643             hideParent:true
14644         };
14645         if(typeof config == "string"){
14646             bconfig.text = config;
14647         }else{
14648             if(config.tag){
14649                 bconfig.dhconfig = config;
14650             }else{
14651                 Roo.apply(bconfig, config);
14652             }
14653         }
14654         var fc = false;
14655         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14656             bconfig.position = Math.max(0, bconfig.position);
14657             fc = this.btnContainer.childNodes[bconfig.position];
14658         }
14659          
14660         var btn = new Roo.Button(
14661             fc ? 
14662                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14663                 : this.btnContainer.appendChild(document.createElement("td")),
14664             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14665             bconfig
14666         );
14667         this.syncBodyHeight();
14668         if(!this.buttons){
14669             /**
14670              * Array of all the buttons that have been added to this dialog via addButton
14671              * @type Array
14672              */
14673             this.buttons = [];
14674         }
14675         this.buttons.push(btn);
14676         return btn;
14677     },
14678
14679     /**
14680      * Sets the default button to be focused when the dialog is displayed.
14681      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14682      * @return {Roo.BasicDialog} this
14683      */
14684     setDefaultButton : function(btn){
14685         this.defaultButton = btn;
14686         return this;
14687     },
14688
14689     // private
14690     getHeaderFooterHeight : function(safe){
14691         var height = 0;
14692         if(this.header){
14693            height += this.header.getHeight();
14694         }
14695         if(this.footer){
14696            var fm = this.footer.getMargins();
14697             height += (this.footer.getHeight()+fm.top+fm.bottom);
14698         }
14699         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14700         height += this.centerBg.getPadding("tb");
14701         return height;
14702     },
14703
14704     // private
14705     syncBodyHeight : function(){
14706         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14707         var height = this.size.height - this.getHeaderFooterHeight(false);
14708         bd.setHeight(height-bd.getMargins("tb"));
14709         var hh = this.header.getHeight();
14710         var h = this.size.height-hh;
14711         cb.setHeight(h);
14712         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14713         bw.setHeight(h-cb.getPadding("tb"));
14714         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14715         bd.setWidth(bw.getWidth(true));
14716         if(this.tabs){
14717             this.tabs.syncHeight();
14718             if(Roo.isIE){
14719                 this.tabs.el.repaint();
14720             }
14721         }
14722     },
14723
14724     /**
14725      * Restores the previous state of the dialog if Roo.state is configured.
14726      * @return {Roo.BasicDialog} this
14727      */
14728     restoreState : function(){
14729         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14730         if(box && box.width){
14731             this.xy = [box.x, box.y];
14732             this.resizeTo(box.width, box.height);
14733         }
14734         return this;
14735     },
14736
14737     // private
14738     beforeShow : function(){
14739         this.expand();
14740         if(this.fixedcenter){
14741             this.xy = this.el.getCenterXY(true);
14742         }
14743         if(this.modal){
14744             Roo.get(document.body).addClass("x-body-masked");
14745             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14746             this.mask.show();
14747         }
14748         this.constrainXY();
14749     },
14750
14751     // private
14752     animShow : function(){
14753         var b = Roo.get(this.animateTarget).getBox();
14754         this.proxy.setSize(b.width, b.height);
14755         this.proxy.setLocation(b.x, b.y);
14756         this.proxy.show();
14757         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14758                     true, .35, this.showEl.createDelegate(this));
14759     },
14760
14761     /**
14762      * Shows the dialog.
14763      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14764      * @return {Roo.BasicDialog} this
14765      */
14766     show : function(animateTarget){
14767         if (this.fireEvent("beforeshow", this) === false){
14768             return;
14769         }
14770         if(this.syncHeightBeforeShow){
14771             this.syncBodyHeight();
14772         }else if(this.firstShow){
14773             this.firstShow = false;
14774             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14775         }
14776         this.animateTarget = animateTarget || this.animateTarget;
14777         if(!this.el.isVisible()){
14778             this.beforeShow();
14779             if(this.animateTarget && Roo.get(this.animateTarget)){
14780                 this.animShow();
14781             }else{
14782                 this.showEl();
14783             }
14784         }
14785         return this;
14786     },
14787
14788     // private
14789     showEl : function(){
14790         this.proxy.hide();
14791         this.el.setXY(this.xy);
14792         this.el.show();
14793         this.adjustAssets(true);
14794         this.toFront();
14795         this.focus();
14796         // IE peekaboo bug - fix found by Dave Fenwick
14797         if(Roo.isIE){
14798             this.el.repaint();
14799         }
14800         this.fireEvent("show", this);
14801     },
14802
14803     /**
14804      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14805      * dialog itself will receive focus.
14806      */
14807     focus : function(){
14808         if(this.defaultButton){
14809             this.defaultButton.focus();
14810         }else{
14811             this.focusEl.focus();
14812         }
14813     },
14814
14815     // private
14816     constrainXY : function(){
14817         if(this.constraintoviewport !== false){
14818             if(!this.viewSize){
14819                 if(this.container){
14820                     var s = this.container.getSize();
14821                     this.viewSize = [s.width, s.height];
14822                 }else{
14823                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14824                 }
14825             }
14826             var s = Roo.get(this.container||document).getScroll();
14827
14828             var x = this.xy[0], y = this.xy[1];
14829             var w = this.size.width, h = this.size.height;
14830             var vw = this.viewSize[0], vh = this.viewSize[1];
14831             // only move it if it needs it
14832             var moved = false;
14833             // first validate right/bottom
14834             if(x + w > vw+s.left){
14835                 x = vw - w;
14836                 moved = true;
14837             }
14838             if(y + h > vh+s.top){
14839                 y = vh - h;
14840                 moved = true;
14841             }
14842             // then make sure top/left isn't negative
14843             if(x < s.left){
14844                 x = s.left;
14845                 moved = true;
14846             }
14847             if(y < s.top){
14848                 y = s.top;
14849                 moved = true;
14850             }
14851             if(moved){
14852                 // cache xy
14853                 this.xy = [x, y];
14854                 if(this.isVisible()){
14855                     this.el.setLocation(x, y);
14856                     this.adjustAssets();
14857                 }
14858             }
14859         }
14860     },
14861
14862     // private
14863     onDrag : function(){
14864         if(!this.proxyDrag){
14865             this.xy = this.el.getXY();
14866             this.adjustAssets();
14867         }
14868     },
14869
14870     // private
14871     adjustAssets : function(doShow){
14872         var x = this.xy[0], y = this.xy[1];
14873         var w = this.size.width, h = this.size.height;
14874         if(doShow === true){
14875             if(this.shadow){
14876                 this.shadow.show(this.el);
14877             }
14878             if(this.shim){
14879                 this.shim.show();
14880             }
14881         }
14882         if(this.shadow && this.shadow.isVisible()){
14883             this.shadow.show(this.el);
14884         }
14885         if(this.shim && this.shim.isVisible()){
14886             this.shim.setBounds(x, y, w, h);
14887         }
14888     },
14889
14890     // private
14891     adjustViewport : function(w, h){
14892         if(!w || !h){
14893             w = Roo.lib.Dom.getViewWidth();
14894             h = Roo.lib.Dom.getViewHeight();
14895         }
14896         // cache the size
14897         this.viewSize = [w, h];
14898         if(this.modal && this.mask.isVisible()){
14899             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14900             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14901         }
14902         if(this.isVisible()){
14903             this.constrainXY();
14904         }
14905     },
14906
14907     /**
14908      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14909      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14910      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14911      */
14912     destroy : function(removeEl){
14913         if(this.isVisible()){
14914             this.animateTarget = null;
14915             this.hide();
14916         }
14917         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14918         if(this.tabs){
14919             this.tabs.destroy(removeEl);
14920         }
14921         Roo.destroy(
14922              this.shim,
14923              this.proxy,
14924              this.resizer,
14925              this.close,
14926              this.mask
14927         );
14928         if(this.dd){
14929             this.dd.unreg();
14930         }
14931         if(this.buttons){
14932            for(var i = 0, len = this.buttons.length; i < len; i++){
14933                this.buttons[i].destroy();
14934            }
14935         }
14936         this.el.removeAllListeners();
14937         if(removeEl === true){
14938             this.el.update("");
14939             this.el.remove();
14940         }
14941         Roo.DialogManager.unregister(this);
14942     },
14943
14944     // private
14945     startMove : function(){
14946         if(this.proxyDrag){
14947             this.proxy.show();
14948         }
14949         if(this.constraintoviewport !== false){
14950             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14951         }
14952     },
14953
14954     // private
14955     endMove : function(){
14956         if(!this.proxyDrag){
14957             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14958         }else{
14959             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14960             this.proxy.hide();
14961         }
14962         this.refreshSize();
14963         this.adjustAssets();
14964         this.focus();
14965         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14966     },
14967
14968     /**
14969      * Brings this dialog to the front of any other visible dialogs
14970      * @return {Roo.BasicDialog} this
14971      */
14972     toFront : function(){
14973         Roo.DialogManager.bringToFront(this);
14974         return this;
14975     },
14976
14977     /**
14978      * Sends this dialog to the back (under) of any other visible dialogs
14979      * @return {Roo.BasicDialog} this
14980      */
14981     toBack : function(){
14982         Roo.DialogManager.sendToBack(this);
14983         return this;
14984     },
14985
14986     /**
14987      * Centers this dialog in the viewport
14988      * @return {Roo.BasicDialog} this
14989      */
14990     center : function(){
14991         var xy = this.el.getCenterXY(true);
14992         this.moveTo(xy[0], xy[1]);
14993         return this;
14994     },
14995
14996     /**
14997      * Moves the dialog's top-left corner to the specified point
14998      * @param {Number} x
14999      * @param {Number} y
15000      * @return {Roo.BasicDialog} this
15001      */
15002     moveTo : function(x, y){
15003         this.xy = [x,y];
15004         if(this.isVisible()){
15005             this.el.setXY(this.xy);
15006             this.adjustAssets();
15007         }
15008         return this;
15009     },
15010
15011     /**
15012      * Aligns the dialog to the specified element
15013      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15014      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15015      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15016      * @return {Roo.BasicDialog} this
15017      */
15018     alignTo : function(element, position, offsets){
15019         this.xy = this.el.getAlignToXY(element, position, offsets);
15020         if(this.isVisible()){
15021             this.el.setXY(this.xy);
15022             this.adjustAssets();
15023         }
15024         return this;
15025     },
15026
15027     /**
15028      * Anchors an element to another element and realigns it when the window is resized.
15029      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15030      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15031      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15032      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15033      * is a number, it is used as the buffer delay (defaults to 50ms).
15034      * @return {Roo.BasicDialog} this
15035      */
15036     anchorTo : function(el, alignment, offsets, monitorScroll){
15037         var action = function(){
15038             this.alignTo(el, alignment, offsets);
15039         };
15040         Roo.EventManager.onWindowResize(action, this);
15041         var tm = typeof monitorScroll;
15042         if(tm != 'undefined'){
15043             Roo.EventManager.on(window, 'scroll', action, this,
15044                 {buffer: tm == 'number' ? monitorScroll : 50});
15045         }
15046         action.call(this);
15047         return this;
15048     },
15049
15050     /**
15051      * Returns true if the dialog is visible
15052      * @return {Boolean}
15053      */
15054     isVisible : function(){
15055         return this.el.isVisible();
15056     },
15057
15058     // private
15059     animHide : function(callback){
15060         var b = Roo.get(this.animateTarget).getBox();
15061         this.proxy.show();
15062         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15063         this.el.hide();
15064         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15065                     this.hideEl.createDelegate(this, [callback]));
15066     },
15067
15068     /**
15069      * Hides the dialog.
15070      * @param {Function} callback (optional) Function to call when the dialog is hidden
15071      * @return {Roo.BasicDialog} this
15072      */
15073     hide : function(callback){
15074         if (this.fireEvent("beforehide", this) === false){
15075             return;
15076         }
15077         if(this.shadow){
15078             this.shadow.hide();
15079         }
15080         if(this.shim) {
15081           this.shim.hide();
15082         }
15083         // sometimes animateTarget seems to get set.. causing problems...
15084         // this just double checks..
15085         if(this.animateTarget && Roo.get(this.animateTarget)) {
15086            this.animHide(callback);
15087         }else{
15088             this.el.hide();
15089             this.hideEl(callback);
15090         }
15091         return this;
15092     },
15093
15094     // private
15095     hideEl : function(callback){
15096         this.proxy.hide();
15097         if(this.modal){
15098             this.mask.hide();
15099             Roo.get(document.body).removeClass("x-body-masked");
15100         }
15101         this.fireEvent("hide", this);
15102         if(typeof callback == "function"){
15103             callback();
15104         }
15105     },
15106
15107     // private
15108     hideAction : function(){
15109         this.setLeft("-10000px");
15110         this.setTop("-10000px");
15111         this.setStyle("visibility", "hidden");
15112     },
15113
15114     // private
15115     refreshSize : function(){
15116         this.size = this.el.getSize();
15117         this.xy = this.el.getXY();
15118         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15119     },
15120
15121     // private
15122     // z-index is managed by the DialogManager and may be overwritten at any time
15123     setZIndex : function(index){
15124         if(this.modal){
15125             this.mask.setStyle("z-index", index);
15126         }
15127         if(this.shim){
15128             this.shim.setStyle("z-index", ++index);
15129         }
15130         if(this.shadow){
15131             this.shadow.setZIndex(++index);
15132         }
15133         this.el.setStyle("z-index", ++index);
15134         if(this.proxy){
15135             this.proxy.setStyle("z-index", ++index);
15136         }
15137         if(this.resizer){
15138             this.resizer.proxy.setStyle("z-index", ++index);
15139         }
15140
15141         this.lastZIndex = index;
15142     },
15143
15144     /**
15145      * Returns the element for this dialog
15146      * @return {Roo.Element} The underlying dialog Element
15147      */
15148     getEl : function(){
15149         return this.el;
15150     }
15151 });
15152
15153 /**
15154  * @class Roo.DialogManager
15155  * Provides global access to BasicDialogs that have been created and
15156  * support for z-indexing (layering) multiple open dialogs.
15157  */
15158 Roo.DialogManager = function(){
15159     var list = {};
15160     var accessList = [];
15161     var front = null;
15162
15163     // private
15164     var sortDialogs = function(d1, d2){
15165         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15166     };
15167
15168     // private
15169     var orderDialogs = function(){
15170         accessList.sort(sortDialogs);
15171         var seed = Roo.DialogManager.zseed;
15172         for(var i = 0, len = accessList.length; i < len; i++){
15173             var dlg = accessList[i];
15174             if(dlg){
15175                 dlg.setZIndex(seed + (i*10));
15176             }
15177         }
15178     };
15179
15180     return {
15181         /**
15182          * The starting z-index for BasicDialogs (defaults to 9000)
15183          * @type Number The z-index value
15184          */
15185         zseed : 9000,
15186
15187         // private
15188         register : function(dlg){
15189             list[dlg.id] = dlg;
15190             accessList.push(dlg);
15191         },
15192
15193         // private
15194         unregister : function(dlg){
15195             delete list[dlg.id];
15196             var i=0;
15197             var len=0;
15198             if(!accessList.indexOf){
15199                 for(  i = 0, len = accessList.length; i < len; i++){
15200                     if(accessList[i] == dlg){
15201                         accessList.splice(i, 1);
15202                         return;
15203                     }
15204                 }
15205             }else{
15206                  i = accessList.indexOf(dlg);
15207                 if(i != -1){
15208                     accessList.splice(i, 1);
15209                 }
15210             }
15211         },
15212
15213         /**
15214          * Gets a registered dialog by id
15215          * @param {String/Object} id The id of the dialog or a dialog
15216          * @return {Roo.BasicDialog} this
15217          */
15218         get : function(id){
15219             return typeof id == "object" ? id : list[id];
15220         },
15221
15222         /**
15223          * Brings the specified dialog to the front
15224          * @param {String/Object} dlg The id of the dialog or a dialog
15225          * @return {Roo.BasicDialog} this
15226          */
15227         bringToFront : function(dlg){
15228             dlg = this.get(dlg);
15229             if(dlg != front){
15230                 front = dlg;
15231                 dlg._lastAccess = new Date().getTime();
15232                 orderDialogs();
15233             }
15234             return dlg;
15235         },
15236
15237         /**
15238          * Sends the specified dialog to the back
15239          * @param {String/Object} dlg The id of the dialog or a dialog
15240          * @return {Roo.BasicDialog} this
15241          */
15242         sendToBack : function(dlg){
15243             dlg = this.get(dlg);
15244             dlg._lastAccess = -(new Date().getTime());
15245             orderDialogs();
15246             return dlg;
15247         },
15248
15249         /**
15250          * Hides all dialogs
15251          */
15252         hideAll : function(){
15253             for(var id in list){
15254                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15255                     list[id].hide();
15256                 }
15257             }
15258         }
15259     };
15260 }();
15261
15262 /**
15263  * @class Roo.LayoutDialog
15264  * @extends Roo.BasicDialog
15265  * Dialog which provides adjustments for working with a layout in a Dialog.
15266  * Add your necessary layout config options to the dialog's config.<br>
15267  * Example usage (including a nested layout):
15268  * <pre><code>
15269 if(!dialog){
15270     dialog = new Roo.LayoutDialog("download-dlg", {
15271         modal: true,
15272         width:600,
15273         height:450,
15274         shadow:true,
15275         minWidth:500,
15276         minHeight:350,
15277         autoTabs:true,
15278         proxyDrag:true,
15279         // layout config merges with the dialog config
15280         center:{
15281             tabPosition: "top",
15282             alwaysShowTabs: true
15283         }
15284     });
15285     dialog.addKeyListener(27, dialog.hide, dialog);
15286     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15287     dialog.addButton("Build It!", this.getDownload, this);
15288
15289     // we can even add nested layouts
15290     var innerLayout = new Roo.BorderLayout("dl-inner", {
15291         east: {
15292             initialSize: 200,
15293             autoScroll:true,
15294             split:true
15295         },
15296         center: {
15297             autoScroll:true
15298         }
15299     });
15300     innerLayout.beginUpdate();
15301     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15302     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15303     innerLayout.endUpdate(true);
15304
15305     var layout = dialog.getLayout();
15306     layout.beginUpdate();
15307     layout.add("center", new Roo.ContentPanel("standard-panel",
15308                         {title: "Download the Source", fitToFrame:true}));
15309     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15310                {title: "Build your own roo.js"}));
15311     layout.getRegion("center").showPanel(sp);
15312     layout.endUpdate();
15313 }
15314 </code></pre>
15315     * @constructor
15316     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15317     * @param {Object} config configuration options
15318   */
15319 Roo.LayoutDialog = function(el, cfg){
15320     
15321     var config=  cfg;
15322     if (typeof(cfg) == 'undefined') {
15323         config = Roo.apply({}, el);
15324         // not sure why we use documentElement here.. - it should always be body.
15325         // IE7 borks horribly if we use documentElement.
15326         // webkit also does not like documentElement - it creates a body element...
15327         el = Roo.get( document.body || document.documentElement ).createChild();
15328         //config.autoCreate = true;
15329     }
15330     
15331     
15332     config.autoTabs = false;
15333     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15334     this.body.setStyle({overflow:"hidden", position:"relative"});
15335     this.layout = new Roo.BorderLayout(this.body.dom, config);
15336     this.layout.monitorWindowResize = false;
15337     this.el.addClass("x-dlg-auto-layout");
15338     // fix case when center region overwrites center function
15339     this.center = Roo.BasicDialog.prototype.center;
15340     this.on("show", this.layout.layout, this.layout, true);
15341     if (config.items) {
15342         var xitems = config.items;
15343         delete config.items;
15344         Roo.each(xitems, this.addxtype, this);
15345     }
15346     
15347     
15348 };
15349 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15350     /**
15351      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15352      * @deprecated
15353      */
15354     endUpdate : function(){
15355         this.layout.endUpdate();
15356     },
15357
15358     /**
15359      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15360      *  @deprecated
15361      */
15362     beginUpdate : function(){
15363         this.layout.beginUpdate();
15364     },
15365
15366     /**
15367      * Get the BorderLayout for this dialog
15368      * @return {Roo.BorderLayout}
15369      */
15370     getLayout : function(){
15371         return this.layout;
15372     },
15373
15374     showEl : function(){
15375         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15376         if(Roo.isIE7){
15377             this.layout.layout();
15378         }
15379     },
15380
15381     // private
15382     // Use the syncHeightBeforeShow config option to control this automatically
15383     syncBodyHeight : function(){
15384         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15385         if(this.layout){this.layout.layout();}
15386     },
15387     
15388       /**
15389      * Add an xtype element (actually adds to the layout.)
15390      * @return {Object} xdata xtype object data.
15391      */
15392     
15393     addxtype : function(c) {
15394         return this.layout.addxtype(c);
15395     }
15396 });/*
15397  * Based on:
15398  * Ext JS Library 1.1.1
15399  * Copyright(c) 2006-2007, Ext JS, LLC.
15400  *
15401  * Originally Released Under LGPL - original licence link has changed is not relivant.
15402  *
15403  * Fork - LGPL
15404  * <script type="text/javascript">
15405  */
15406  
15407 /**
15408  * @class Roo.MessageBox
15409  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15410  * Example usage:
15411  *<pre><code>
15412 // Basic alert:
15413 Roo.Msg.alert('Status', 'Changes saved successfully.');
15414
15415 // Prompt for user data:
15416 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15417     if (btn == 'ok'){
15418         // process text value...
15419     }
15420 });
15421
15422 // Show a dialog using config options:
15423 Roo.Msg.show({
15424    title:'Save Changes?',
15425    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15426    buttons: Roo.Msg.YESNOCANCEL,
15427    fn: processResult,
15428    animEl: 'elId'
15429 });
15430 </code></pre>
15431  * @singleton
15432  */
15433 Roo.MessageBox = function(){
15434     var dlg, opt, mask, waitTimer;
15435     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15436     var buttons, activeTextEl, bwidth;
15437
15438     // private
15439     var handleButton = function(button){
15440         dlg.hide();
15441         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15442     };
15443
15444     // private
15445     var handleHide = function(){
15446         if(opt && opt.cls){
15447             dlg.el.removeClass(opt.cls);
15448         }
15449         if(waitTimer){
15450             Roo.TaskMgr.stop(waitTimer);
15451             waitTimer = null;
15452         }
15453     };
15454
15455     // private
15456     var updateButtons = function(b){
15457         var width = 0;
15458         if(!b){
15459             buttons["ok"].hide();
15460             buttons["cancel"].hide();
15461             buttons["yes"].hide();
15462             buttons["no"].hide();
15463             dlg.footer.dom.style.display = 'none';
15464             return width;
15465         }
15466         dlg.footer.dom.style.display = '';
15467         for(var k in buttons){
15468             if(typeof buttons[k] != "function"){
15469                 if(b[k]){
15470                     buttons[k].show();
15471                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15472                     width += buttons[k].el.getWidth()+15;
15473                 }else{
15474                     buttons[k].hide();
15475                 }
15476             }
15477         }
15478         return width;
15479     };
15480
15481     // private
15482     var handleEsc = function(d, k, e){
15483         if(opt && opt.closable !== false){
15484             dlg.hide();
15485         }
15486         if(e){
15487             e.stopEvent();
15488         }
15489     };
15490
15491     return {
15492         /**
15493          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15494          * @return {Roo.BasicDialog} The BasicDialog element
15495          */
15496         getDialog : function(){
15497            if(!dlg){
15498                 dlg = new Roo.BasicDialog("x-msg-box", {
15499                     autoCreate : true,
15500                     shadow: true,
15501                     draggable: true,
15502                     resizable:false,
15503                     constraintoviewport:false,
15504                     fixedcenter:true,
15505                     collapsible : false,
15506                     shim:true,
15507                     modal: true,
15508                     width:400, height:100,
15509                     buttonAlign:"center",
15510                     closeClick : function(){
15511                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15512                             handleButton("no");
15513                         }else{
15514                             handleButton("cancel");
15515                         }
15516                     }
15517                 });
15518                 dlg.on("hide", handleHide);
15519                 mask = dlg.mask;
15520                 dlg.addKeyListener(27, handleEsc);
15521                 buttons = {};
15522                 var bt = this.buttonText;
15523                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15524                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15525                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15526                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15527                 bodyEl = dlg.body.createChild({
15528
15529                     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>'
15530                 });
15531                 msgEl = bodyEl.dom.firstChild;
15532                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15533                 textboxEl.enableDisplayMode();
15534                 textboxEl.addKeyListener([10,13], function(){
15535                     if(dlg.isVisible() && opt && opt.buttons){
15536                         if(opt.buttons.ok){
15537                             handleButton("ok");
15538                         }else if(opt.buttons.yes){
15539                             handleButton("yes");
15540                         }
15541                     }
15542                 });
15543                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15544                 textareaEl.enableDisplayMode();
15545                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15546                 progressEl.enableDisplayMode();
15547                 var pf = progressEl.dom.firstChild;
15548                 if (pf) {
15549                     pp = Roo.get(pf.firstChild);
15550                     pp.setHeight(pf.offsetHeight);
15551                 }
15552                 
15553             }
15554             return dlg;
15555         },
15556
15557         /**
15558          * Updates the message box body text
15559          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15560          * the XHTML-compliant non-breaking space character '&amp;#160;')
15561          * @return {Roo.MessageBox} This message box
15562          */
15563         updateText : function(text){
15564             if(!dlg.isVisible() && !opt.width){
15565                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15566             }
15567             msgEl.innerHTML = text || '&#160;';
15568             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15569                         Math.max(opt.minWidth || this.minWidth, bwidth));
15570             if(opt.prompt){
15571                 activeTextEl.setWidth(w);
15572             }
15573             if(dlg.isVisible()){
15574                 dlg.fixedcenter = false;
15575             }
15576             dlg.setContentSize(w, bodyEl.getHeight());
15577             if(dlg.isVisible()){
15578                 dlg.fixedcenter = true;
15579             }
15580             return this;
15581         },
15582
15583         /**
15584          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15585          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15586          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15587          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15588          * @return {Roo.MessageBox} This message box
15589          */
15590         updateProgress : function(value, text){
15591             if(text){
15592                 this.updateText(text);
15593             }
15594             if (pp) { // weird bug on my firefox - for some reason this is not defined
15595                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15596             }
15597             return this;
15598         },        
15599
15600         /**
15601          * Returns true if the message box is currently displayed
15602          * @return {Boolean} True if the message box is visible, else false
15603          */
15604         isVisible : function(){
15605             return dlg && dlg.isVisible();  
15606         },
15607
15608         /**
15609          * Hides the message box if it is displayed
15610          */
15611         hide : function(){
15612             if(this.isVisible()){
15613                 dlg.hide();
15614             }  
15615         },
15616
15617         /**
15618          * Displays a new message box, or reinitializes an existing message box, based on the config options
15619          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15620          * The following config object properties are supported:
15621          * <pre>
15622 Property    Type             Description
15623 ----------  ---------------  ------------------------------------------------------------------------------------
15624 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15625                                    closes (defaults to undefined)
15626 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15627                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15628 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15629                                    progress and wait dialogs will ignore this property and always hide the
15630                                    close button as they can only be closed programmatically.
15631 cls               String           A custom CSS class to apply to the message box element
15632 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15633                                    displayed (defaults to 75)
15634 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15635                                    function will be btn (the name of the button that was clicked, if applicable,
15636                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15637                                    Progress and wait dialogs will ignore this option since they do not respond to
15638                                    user actions and can only be closed programmatically, so any required function
15639                                    should be called by the same code after it closes the dialog.
15640 icon              String           A CSS class that provides a background image to be used as an icon for
15641                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15642 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15643 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15644 modal             Boolean          False to allow user interaction with the page while the message box is
15645                                    displayed (defaults to true)
15646 msg               String           A string that will replace the existing message box body text (defaults
15647                                    to the XHTML-compliant non-breaking space character '&#160;')
15648 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15649 progress          Boolean          True to display a progress bar (defaults to false)
15650 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15651 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15652 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15653 title             String           The title text
15654 value             String           The string value to set into the active textbox element if displayed
15655 wait              Boolean          True to display a progress bar (defaults to false)
15656 width             Number           The width of the dialog in pixels
15657 </pre>
15658          *
15659          * Example usage:
15660          * <pre><code>
15661 Roo.Msg.show({
15662    title: 'Address',
15663    msg: 'Please enter your address:',
15664    width: 300,
15665    buttons: Roo.MessageBox.OKCANCEL,
15666    multiline: true,
15667    fn: saveAddress,
15668    animEl: 'addAddressBtn'
15669 });
15670 </code></pre>
15671          * @param {Object} config Configuration options
15672          * @return {Roo.MessageBox} This message box
15673          */
15674         show : function(options){
15675             if(this.isVisible()){
15676                 this.hide();
15677             }
15678             var d = this.getDialog();
15679             opt = options;
15680             d.setTitle(opt.title || "&#160;");
15681             d.close.setDisplayed(opt.closable !== false);
15682             activeTextEl = textboxEl;
15683             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15684             if(opt.prompt){
15685                 if(opt.multiline){
15686                     textboxEl.hide();
15687                     textareaEl.show();
15688                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15689                         opt.multiline : this.defaultTextHeight);
15690                     activeTextEl = textareaEl;
15691                 }else{
15692                     textboxEl.show();
15693                     textareaEl.hide();
15694                 }
15695             }else{
15696                 textboxEl.hide();
15697                 textareaEl.hide();
15698             }
15699             progressEl.setDisplayed(opt.progress === true);
15700             this.updateProgress(0);
15701             activeTextEl.dom.value = opt.value || "";
15702             if(opt.prompt){
15703                 dlg.setDefaultButton(activeTextEl);
15704             }else{
15705                 var bs = opt.buttons;
15706                 var db = null;
15707                 if(bs && bs.ok){
15708                     db = buttons["ok"];
15709                 }else if(bs && bs.yes){
15710                     db = buttons["yes"];
15711                 }
15712                 dlg.setDefaultButton(db);
15713             }
15714             bwidth = updateButtons(opt.buttons);
15715             this.updateText(opt.msg);
15716             if(opt.cls){
15717                 d.el.addClass(opt.cls);
15718             }
15719             d.proxyDrag = opt.proxyDrag === true;
15720             d.modal = opt.modal !== false;
15721             d.mask = opt.modal !== false ? mask : false;
15722             if(!d.isVisible()){
15723                 // force it to the end of the z-index stack so it gets a cursor in FF
15724                 document.body.appendChild(dlg.el.dom);
15725                 d.animateTarget = null;
15726                 d.show(options.animEl);
15727             }
15728             return this;
15729         },
15730
15731         /**
15732          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15733          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15734          * and closing the message box when the process is complete.
15735          * @param {String} title The title bar text
15736          * @param {String} msg The message box body text
15737          * @return {Roo.MessageBox} This message box
15738          */
15739         progress : function(title, msg){
15740             this.show({
15741                 title : title,
15742                 msg : msg,
15743                 buttons: false,
15744                 progress:true,
15745                 closable:false,
15746                 minWidth: this.minProgressWidth,
15747                 modal : true
15748             });
15749             return this;
15750         },
15751
15752         /**
15753          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15754          * If a callback function is passed it will be called after the user clicks the button, and the
15755          * id of the button that was clicked will be passed as the only parameter to the callback
15756          * (could also be the top-right close button).
15757          * @param {String} title The title bar text
15758          * @param {String} msg The message box body text
15759          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15760          * @param {Object} scope (optional) The scope of the callback function
15761          * @return {Roo.MessageBox} This message box
15762          */
15763         alert : function(title, msg, fn, scope){
15764             this.show({
15765                 title : title,
15766                 msg : msg,
15767                 buttons: this.OK,
15768                 fn: fn,
15769                 scope : scope,
15770                 modal : true
15771             });
15772             return this;
15773         },
15774
15775         /**
15776          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15777          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15778          * You are responsible for closing the message box when the process is complete.
15779          * @param {String} msg The message box body text
15780          * @param {String} title (optional) The title bar text
15781          * @return {Roo.MessageBox} This message box
15782          */
15783         wait : function(msg, title){
15784             this.show({
15785                 title : title,
15786                 msg : msg,
15787                 buttons: false,
15788                 closable:false,
15789                 progress:true,
15790                 modal:true,
15791                 width:300,
15792                 wait:true
15793             });
15794             waitTimer = Roo.TaskMgr.start({
15795                 run: function(i){
15796                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15797                 },
15798                 interval: 1000
15799             });
15800             return this;
15801         },
15802
15803         /**
15804          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15805          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15806          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15807          * @param {String} title The title bar text
15808          * @param {String} msg The message box body text
15809          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15810          * @param {Object} scope (optional) The scope of the callback function
15811          * @return {Roo.MessageBox} This message box
15812          */
15813         confirm : function(title, msg, fn, scope){
15814             this.show({
15815                 title : title,
15816                 msg : msg,
15817                 buttons: this.YESNO,
15818                 fn: fn,
15819                 scope : scope,
15820                 modal : true
15821             });
15822             return this;
15823         },
15824
15825         /**
15826          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15827          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15828          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15829          * (could also be the top-right close button) and the text that was entered will be passed as the two
15830          * parameters to the callback.
15831          * @param {String} title The title bar text
15832          * @param {String} msg The message box body text
15833          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15834          * @param {Object} scope (optional) The scope of the callback function
15835          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15836          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15837          * @return {Roo.MessageBox} This message box
15838          */
15839         prompt : function(title, msg, fn, scope, multiline){
15840             this.show({
15841                 title : title,
15842                 msg : msg,
15843                 buttons: this.OKCANCEL,
15844                 fn: fn,
15845                 minWidth:250,
15846                 scope : scope,
15847                 prompt:true,
15848                 multiline: multiline,
15849                 modal : true
15850             });
15851             return this;
15852         },
15853
15854         /**
15855          * Button config that displays a single OK button
15856          * @type Object
15857          */
15858         OK : {ok:true},
15859         /**
15860          * Button config that displays Yes and No buttons
15861          * @type Object
15862          */
15863         YESNO : {yes:true, no:true},
15864         /**
15865          * Button config that displays OK and Cancel buttons
15866          * @type Object
15867          */
15868         OKCANCEL : {ok:true, cancel:true},
15869         /**
15870          * Button config that displays Yes, No and Cancel buttons
15871          * @type Object
15872          */
15873         YESNOCANCEL : {yes:true, no:true, cancel:true},
15874
15875         /**
15876          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15877          * @type Number
15878          */
15879         defaultTextHeight : 75,
15880         /**
15881          * The maximum width in pixels of the message box (defaults to 600)
15882          * @type Number
15883          */
15884         maxWidth : 600,
15885         /**
15886          * The minimum width in pixels of the message box (defaults to 100)
15887          * @type Number
15888          */
15889         minWidth : 100,
15890         /**
15891          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15892          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15893          * @type Number
15894          */
15895         minProgressWidth : 250,
15896         /**
15897          * An object containing the default button text strings that can be overriden for localized language support.
15898          * Supported properties are: ok, cancel, yes and no.
15899          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15900          * @type Object
15901          */
15902         buttonText : {
15903             ok : "OK",
15904             cancel : "Cancel",
15905             yes : "Yes",
15906             no : "No"
15907         }
15908     };
15909 }();
15910
15911 /**
15912  * Shorthand for {@link Roo.MessageBox}
15913  */
15914 Roo.Msg = Roo.MessageBox;/*
15915  * Based on:
15916  * Ext JS Library 1.1.1
15917  * Copyright(c) 2006-2007, Ext JS, LLC.
15918  *
15919  * Originally Released Under LGPL - original licence link has changed is not relivant.
15920  *
15921  * Fork - LGPL
15922  * <script type="text/javascript">
15923  */
15924 /**
15925  * @class Roo.QuickTips
15926  * Provides attractive and customizable tooltips for any element.
15927  * @singleton
15928  */
15929 Roo.QuickTips = function(){
15930     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15931     var ce, bd, xy, dd;
15932     var visible = false, disabled = true, inited = false;
15933     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15934     
15935     var onOver = function(e){
15936         if(disabled){
15937             return;
15938         }
15939         var t = e.getTarget();
15940         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15941             return;
15942         }
15943         if(ce && t == ce.el){
15944             clearTimeout(hideProc);
15945             return;
15946         }
15947         if(t && tagEls[t.id]){
15948             tagEls[t.id].el = t;
15949             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15950             return;
15951         }
15952         var ttp, et = Roo.fly(t);
15953         var ns = cfg.namespace;
15954         if(tm.interceptTitles && t.title){
15955             ttp = t.title;
15956             t.qtip = ttp;
15957             t.removeAttribute("title");
15958             e.preventDefault();
15959         }else{
15960             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15961         }
15962         if(ttp){
15963             showProc = show.defer(tm.showDelay, tm, [{
15964                 el: t, 
15965                 text: ttp, 
15966                 width: et.getAttributeNS(ns, cfg.width),
15967                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15968                 title: et.getAttributeNS(ns, cfg.title),
15969                     cls: et.getAttributeNS(ns, cfg.cls)
15970             }]);
15971         }
15972     };
15973     
15974     var onOut = function(e){
15975         clearTimeout(showProc);
15976         var t = e.getTarget();
15977         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15978             hideProc = setTimeout(hide, tm.hideDelay);
15979         }
15980     };
15981     
15982     var onMove = function(e){
15983         if(disabled){
15984             return;
15985         }
15986         xy = e.getXY();
15987         xy[1] += 18;
15988         if(tm.trackMouse && ce){
15989             el.setXY(xy);
15990         }
15991     };
15992     
15993     var onDown = function(e){
15994         clearTimeout(showProc);
15995         clearTimeout(hideProc);
15996         if(!e.within(el)){
15997             if(tm.hideOnClick){
15998                 hide();
15999                 tm.disable();
16000                 tm.enable.defer(100, tm);
16001             }
16002         }
16003     };
16004     
16005     var getPad = function(){
16006         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16007     };
16008
16009     var show = function(o){
16010         if(disabled){
16011             return;
16012         }
16013         clearTimeout(dismissProc);
16014         ce = o;
16015         if(removeCls){ // in case manually hidden
16016             el.removeClass(removeCls);
16017             removeCls = null;
16018         }
16019         if(ce.cls){
16020             el.addClass(ce.cls);
16021             removeCls = ce.cls;
16022         }
16023         if(ce.title){
16024             tipTitle.update(ce.title);
16025             tipTitle.show();
16026         }else{
16027             tipTitle.update('');
16028             tipTitle.hide();
16029         }
16030         el.dom.style.width  = tm.maxWidth+'px';
16031         //tipBody.dom.style.width = '';
16032         tipBodyText.update(o.text);
16033         var p = getPad(), w = ce.width;
16034         if(!w){
16035             var td = tipBodyText.dom;
16036             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16037             if(aw > tm.maxWidth){
16038                 w = tm.maxWidth;
16039             }else if(aw < tm.minWidth){
16040                 w = tm.minWidth;
16041             }else{
16042                 w = aw;
16043             }
16044         }
16045         //tipBody.setWidth(w);
16046         el.setWidth(parseInt(w, 10) + p);
16047         if(ce.autoHide === false){
16048             close.setDisplayed(true);
16049             if(dd){
16050                 dd.unlock();
16051             }
16052         }else{
16053             close.setDisplayed(false);
16054             if(dd){
16055                 dd.lock();
16056             }
16057         }
16058         if(xy){
16059             el.avoidY = xy[1]-18;
16060             el.setXY(xy);
16061         }
16062         if(tm.animate){
16063             el.setOpacity(.1);
16064             el.setStyle("visibility", "visible");
16065             el.fadeIn({callback: afterShow});
16066         }else{
16067             afterShow();
16068         }
16069     };
16070     
16071     var afterShow = function(){
16072         if(ce){
16073             el.show();
16074             esc.enable();
16075             if(tm.autoDismiss && ce.autoHide !== false){
16076                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16077             }
16078         }
16079     };
16080     
16081     var hide = function(noanim){
16082         clearTimeout(dismissProc);
16083         clearTimeout(hideProc);
16084         ce = null;
16085         if(el.isVisible()){
16086             esc.disable();
16087             if(noanim !== true && tm.animate){
16088                 el.fadeOut({callback: afterHide});
16089             }else{
16090                 afterHide();
16091             } 
16092         }
16093     };
16094     
16095     var afterHide = function(){
16096         el.hide();
16097         if(removeCls){
16098             el.removeClass(removeCls);
16099             removeCls = null;
16100         }
16101     };
16102     
16103     return {
16104         /**
16105         * @cfg {Number} minWidth
16106         * The minimum width of the quick tip (defaults to 40)
16107         */
16108        minWidth : 40,
16109         /**
16110         * @cfg {Number} maxWidth
16111         * The maximum width of the quick tip (defaults to 300)
16112         */
16113        maxWidth : 300,
16114         /**
16115         * @cfg {Boolean} interceptTitles
16116         * True to automatically use the element's DOM title value if available (defaults to false)
16117         */
16118        interceptTitles : false,
16119         /**
16120         * @cfg {Boolean} trackMouse
16121         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16122         */
16123        trackMouse : false,
16124         /**
16125         * @cfg {Boolean} hideOnClick
16126         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16127         */
16128        hideOnClick : true,
16129         /**
16130         * @cfg {Number} showDelay
16131         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16132         */
16133        showDelay : 500,
16134         /**
16135         * @cfg {Number} hideDelay
16136         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16137         */
16138        hideDelay : 200,
16139         /**
16140         * @cfg {Boolean} autoHide
16141         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16142         * Used in conjunction with hideDelay.
16143         */
16144        autoHide : true,
16145         /**
16146         * @cfg {Boolean}
16147         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16148         * (defaults to true).  Used in conjunction with autoDismissDelay.
16149         */
16150        autoDismiss : true,
16151         /**
16152         * @cfg {Number}
16153         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16154         */
16155        autoDismissDelay : 5000,
16156        /**
16157         * @cfg {Boolean} animate
16158         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16159         */
16160        animate : false,
16161
16162        /**
16163         * @cfg {String} title
16164         * Title text to display (defaults to '').  This can be any valid HTML markup.
16165         */
16166         title: '',
16167        /**
16168         * @cfg {String} text
16169         * Body text to display (defaults to '').  This can be any valid HTML markup.
16170         */
16171         text : '',
16172        /**
16173         * @cfg {String} cls
16174         * A CSS class to apply to the base quick tip element (defaults to '').
16175         */
16176         cls : '',
16177        /**
16178         * @cfg {Number} width
16179         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16180         * minWidth or maxWidth.
16181         */
16182         width : null,
16183
16184     /**
16185      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16186      * or display QuickTips in a page.
16187      */
16188        init : function(){
16189           tm = Roo.QuickTips;
16190           cfg = tm.tagConfig;
16191           if(!inited){
16192               if(!Roo.isReady){ // allow calling of init() before onReady
16193                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16194                   return;
16195               }
16196               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16197               el.fxDefaults = {stopFx: true};
16198               // maximum custom styling
16199               //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>');
16200               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>');              
16201               tipTitle = el.child('h3');
16202               tipTitle.enableDisplayMode("block");
16203               tipBody = el.child('div.x-tip-bd');
16204               tipBodyText = el.child('div.x-tip-bd-inner');
16205               //bdLeft = el.child('div.x-tip-bd-left');
16206               //bdRight = el.child('div.x-tip-bd-right');
16207               close = el.child('div.x-tip-close');
16208               close.enableDisplayMode("block");
16209               close.on("click", hide);
16210               var d = Roo.get(document);
16211               d.on("mousedown", onDown);
16212               d.on("mouseover", onOver);
16213               d.on("mouseout", onOut);
16214               d.on("mousemove", onMove);
16215               esc = d.addKeyListener(27, hide);
16216               esc.disable();
16217               if(Roo.dd.DD){
16218                   dd = el.initDD("default", null, {
16219                       onDrag : function(){
16220                           el.sync();  
16221                       }
16222                   });
16223                   dd.setHandleElId(tipTitle.id);
16224                   dd.lock();
16225               }
16226               inited = true;
16227           }
16228           this.enable(); 
16229        },
16230
16231     /**
16232      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16233      * are supported:
16234      * <pre>
16235 Property    Type                   Description
16236 ----------  ---------------------  ------------------------------------------------------------------------
16237 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16238      * </ul>
16239      * @param {Object} config The config object
16240      */
16241        register : function(config){
16242            var cs = config instanceof Array ? config : arguments;
16243            for(var i = 0, len = cs.length; i < len; i++) {
16244                var c = cs[i];
16245                var target = c.target;
16246                if(target){
16247                    if(target instanceof Array){
16248                        for(var j = 0, jlen = target.length; j < jlen; j++){
16249                            tagEls[target[j]] = c;
16250                        }
16251                    }else{
16252                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16253                    }
16254                }
16255            }
16256        },
16257
16258     /**
16259      * Removes this quick tip from its element and destroys it.
16260      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16261      */
16262        unregister : function(el){
16263            delete tagEls[Roo.id(el)];
16264        },
16265
16266     /**
16267      * Enable this quick tip.
16268      */
16269        enable : function(){
16270            if(inited && disabled){
16271                locks.pop();
16272                if(locks.length < 1){
16273                    disabled = false;
16274                }
16275            }
16276        },
16277
16278     /**
16279      * Disable this quick tip.
16280      */
16281        disable : function(){
16282           disabled = true;
16283           clearTimeout(showProc);
16284           clearTimeout(hideProc);
16285           clearTimeout(dismissProc);
16286           if(ce){
16287               hide(true);
16288           }
16289           locks.push(1);
16290        },
16291
16292     /**
16293      * Returns true if the quick tip is enabled, else false.
16294      */
16295        isEnabled : function(){
16296             return !disabled;
16297        },
16298
16299         // private
16300        tagConfig : {
16301            namespace : "ext",
16302            attribute : "qtip",
16303            width : "width",
16304            target : "target",
16305            title : "qtitle",
16306            hide : "hide",
16307            cls : "qclass"
16308        }
16309    };
16310 }();
16311
16312 // backwards compat
16313 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16314  * Based on:
16315  * Ext JS Library 1.1.1
16316  * Copyright(c) 2006-2007, Ext JS, LLC.
16317  *
16318  * Originally Released Under LGPL - original licence link has changed is not relivant.
16319  *
16320  * Fork - LGPL
16321  * <script type="text/javascript">
16322  */
16323  
16324
16325 /**
16326  * @class Roo.tree.TreePanel
16327  * @extends Roo.data.Tree
16328
16329  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16330  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16331  * @cfg {Boolean} enableDD true to enable drag and drop
16332  * @cfg {Boolean} enableDrag true to enable just drag
16333  * @cfg {Boolean} enableDrop true to enable just drop
16334  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16335  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16336  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16337  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16338  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16339  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16340  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16341  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16342  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16343  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16344  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16345  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16346  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16347  * @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>
16348  * @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>
16349  * 
16350  * @constructor
16351  * @param {String/HTMLElement/Element} el The container element
16352  * @param {Object} config
16353  */
16354 Roo.tree.TreePanel = function(el, config){
16355     var root = false;
16356     var loader = false;
16357     if (config.root) {
16358         root = config.root;
16359         delete config.root;
16360     }
16361     if (config.loader) {
16362         loader = config.loader;
16363         delete config.loader;
16364     }
16365     
16366     Roo.apply(this, config);
16367     Roo.tree.TreePanel.superclass.constructor.call(this);
16368     this.el = Roo.get(el);
16369     this.el.addClass('x-tree');
16370     //console.log(root);
16371     if (root) {
16372         this.setRootNode( Roo.factory(root, Roo.tree));
16373     }
16374     if (loader) {
16375         this.loader = Roo.factory(loader, Roo.tree);
16376     }
16377    /**
16378     * Read-only. The id of the container element becomes this TreePanel's id.
16379     */
16380    this.id = this.el.id;
16381    this.addEvents({
16382         /**
16383         * @event beforeload
16384         * Fires before a node is loaded, return false to cancel
16385         * @param {Node} node The node being loaded
16386         */
16387         "beforeload" : true,
16388         /**
16389         * @event load
16390         * Fires when a node is loaded
16391         * @param {Node} node The node that was loaded
16392         */
16393         "load" : true,
16394         /**
16395         * @event textchange
16396         * Fires when the text for a node is changed
16397         * @param {Node} node The node
16398         * @param {String} text The new text
16399         * @param {String} oldText The old text
16400         */
16401         "textchange" : true,
16402         /**
16403         * @event beforeexpand
16404         * Fires before a node is expanded, return false to cancel.
16405         * @param {Node} node The node
16406         * @param {Boolean} deep
16407         * @param {Boolean} anim
16408         */
16409         "beforeexpand" : true,
16410         /**
16411         * @event beforecollapse
16412         * Fires before a node is collapsed, return false to cancel.
16413         * @param {Node} node The node
16414         * @param {Boolean} deep
16415         * @param {Boolean} anim
16416         */
16417         "beforecollapse" : true,
16418         /**
16419         * @event expand
16420         * Fires when a node is expanded
16421         * @param {Node} node The node
16422         */
16423         "expand" : true,
16424         /**
16425         * @event disabledchange
16426         * Fires when the disabled status of a node changes
16427         * @param {Node} node The node
16428         * @param {Boolean} disabled
16429         */
16430         "disabledchange" : true,
16431         /**
16432         * @event collapse
16433         * Fires when a node is collapsed
16434         * @param {Node} node The node
16435         */
16436         "collapse" : true,
16437         /**
16438         * @event beforeclick
16439         * Fires before click processing on a node. Return false to cancel the default action.
16440         * @param {Node} node The node
16441         * @param {Roo.EventObject} e The event object
16442         */
16443         "beforeclick":true,
16444         /**
16445         * @event checkchange
16446         * Fires when a node with a checkbox's checked property changes
16447         * @param {Node} this This node
16448         * @param {Boolean} checked
16449         */
16450         "checkchange":true,
16451         /**
16452         * @event click
16453         * Fires when a node is clicked
16454         * @param {Node} node The node
16455         * @param {Roo.EventObject} e The event object
16456         */
16457         "click":true,
16458         /**
16459         * @event dblclick
16460         * Fires when a node is double clicked
16461         * @param {Node} node The node
16462         * @param {Roo.EventObject} e The event object
16463         */
16464         "dblclick":true,
16465         /**
16466         * @event contextmenu
16467         * Fires when a node is right clicked
16468         * @param {Node} node The node
16469         * @param {Roo.EventObject} e The event object
16470         */
16471         "contextmenu":true,
16472         /**
16473         * @event beforechildrenrendered
16474         * Fires right before the child nodes for a node are rendered
16475         * @param {Node} node The node
16476         */
16477         "beforechildrenrendered":true,
16478        /**
16479              * @event startdrag
16480              * Fires when a node starts being dragged
16481              * @param {Roo.tree.TreePanel} this
16482              * @param {Roo.tree.TreeNode} node
16483              * @param {event} e The raw browser event
16484              */ 
16485             "startdrag" : true,
16486             /**
16487              * @event enddrag
16488              * Fires when a drag operation is complete
16489              * @param {Roo.tree.TreePanel} this
16490              * @param {Roo.tree.TreeNode} node
16491              * @param {event} e The raw browser event
16492              */
16493             "enddrag" : true,
16494             /**
16495              * @event dragdrop
16496              * Fires when a dragged node is dropped on a valid DD target
16497              * @param {Roo.tree.TreePanel} this
16498              * @param {Roo.tree.TreeNode} node
16499              * @param {DD} dd The dd it was dropped on
16500              * @param {event} e The raw browser event
16501              */
16502             "dragdrop" : true,
16503             /**
16504              * @event beforenodedrop
16505              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16506              * passed to handlers has the following properties:<br />
16507              * <ul style="padding:5px;padding-left:16px;">
16508              * <li>tree - The TreePanel</li>
16509              * <li>target - The node being targeted for the drop</li>
16510              * <li>data - The drag data from the drag source</li>
16511              * <li>point - The point of the drop - append, above or below</li>
16512              * <li>source - The drag source</li>
16513              * <li>rawEvent - Raw mouse event</li>
16514              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16515              * to be inserted by setting them on this object.</li>
16516              * <li>cancel - Set this to true to cancel the drop.</li>
16517              * </ul>
16518              * @param {Object} dropEvent
16519              */
16520             "beforenodedrop" : true,
16521             /**
16522              * @event nodedrop
16523              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16524              * passed to handlers has the following properties:<br />
16525              * <ul style="padding:5px;padding-left:16px;">
16526              * <li>tree - The TreePanel</li>
16527              * <li>target - The node being targeted for the drop</li>
16528              * <li>data - The drag data from the drag source</li>
16529              * <li>point - The point of the drop - append, above or below</li>
16530              * <li>source - The drag source</li>
16531              * <li>rawEvent - Raw mouse event</li>
16532              * <li>dropNode - Dropped node(s).</li>
16533              * </ul>
16534              * @param {Object} dropEvent
16535              */
16536             "nodedrop" : true,
16537              /**
16538              * @event nodedragover
16539              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16540              * passed to handlers has the following properties:<br />
16541              * <ul style="padding:5px;padding-left:16px;">
16542              * <li>tree - The TreePanel</li>
16543              * <li>target - The node being targeted for the drop</li>
16544              * <li>data - The drag data from the drag source</li>
16545              * <li>point - The point of the drop - append, above or below</li>
16546              * <li>source - The drag source</li>
16547              * <li>rawEvent - Raw mouse event</li>
16548              * <li>dropNode - Drop node(s) provided by the source.</li>
16549              * <li>cancel - Set this to true to signal drop not allowed.</li>
16550              * </ul>
16551              * @param {Object} dragOverEvent
16552              */
16553             "nodedragover" : true
16554         
16555    });
16556    if(this.singleExpand){
16557        this.on("beforeexpand", this.restrictExpand, this);
16558    }
16559 };
16560 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16561     rootVisible : true,
16562     animate: Roo.enableFx,
16563     lines : true,
16564     enableDD : false,
16565     hlDrop : Roo.enableFx,
16566   
16567     renderer: false,
16568     
16569     rendererTip: false,
16570     // private
16571     restrictExpand : function(node){
16572         var p = node.parentNode;
16573         if(p){
16574             if(p.expandedChild && p.expandedChild.parentNode == p){
16575                 p.expandedChild.collapse();
16576             }
16577             p.expandedChild = node;
16578         }
16579     },
16580
16581     // private override
16582     setRootNode : function(node){
16583         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16584         if(!this.rootVisible){
16585             node.ui = new Roo.tree.RootTreeNodeUI(node);
16586         }
16587         return node;
16588     },
16589
16590     /**
16591      * Returns the container element for this TreePanel
16592      */
16593     getEl : function(){
16594         return this.el;
16595     },
16596
16597     /**
16598      * Returns the default TreeLoader for this TreePanel
16599      */
16600     getLoader : function(){
16601         return this.loader;
16602     },
16603
16604     /**
16605      * Expand all nodes
16606      */
16607     expandAll : function(){
16608         this.root.expand(true);
16609     },
16610
16611     /**
16612      * Collapse all nodes
16613      */
16614     collapseAll : function(){
16615         this.root.collapse(true);
16616     },
16617
16618     /**
16619      * Returns the selection model used by this TreePanel
16620      */
16621     getSelectionModel : function(){
16622         if(!this.selModel){
16623             this.selModel = new Roo.tree.DefaultSelectionModel();
16624         }
16625         return this.selModel;
16626     },
16627
16628     /**
16629      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16630      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16631      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16632      * @return {Array}
16633      */
16634     getChecked : function(a, startNode){
16635         startNode = startNode || this.root;
16636         var r = [];
16637         var f = function(){
16638             if(this.attributes.checked){
16639                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16640             }
16641         }
16642         startNode.cascade(f);
16643         return r;
16644     },
16645
16646     /**
16647      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16648      * @param {String} path
16649      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16650      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16651      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16652      */
16653     expandPath : function(path, attr, callback){
16654         attr = attr || "id";
16655         var keys = path.split(this.pathSeparator);
16656         var curNode = this.root;
16657         if(curNode.attributes[attr] != keys[1]){ // invalid root
16658             if(callback){
16659                 callback(false, null);
16660             }
16661             return;
16662         }
16663         var index = 1;
16664         var f = function(){
16665             if(++index == keys.length){
16666                 if(callback){
16667                     callback(true, curNode);
16668                 }
16669                 return;
16670             }
16671             var c = curNode.findChild(attr, keys[index]);
16672             if(!c){
16673                 if(callback){
16674                     callback(false, curNode);
16675                 }
16676                 return;
16677             }
16678             curNode = c;
16679             c.expand(false, false, f);
16680         };
16681         curNode.expand(false, false, f);
16682     },
16683
16684     /**
16685      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16686      * @param {String} path
16687      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16688      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16689      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16690      */
16691     selectPath : function(path, attr, callback){
16692         attr = attr || "id";
16693         var keys = path.split(this.pathSeparator);
16694         var v = keys.pop();
16695         if(keys.length > 0){
16696             var f = function(success, node){
16697                 if(success && node){
16698                     var n = node.findChild(attr, v);
16699                     if(n){
16700                         n.select();
16701                         if(callback){
16702                             callback(true, n);
16703                         }
16704                     }else if(callback){
16705                         callback(false, n);
16706                     }
16707                 }else{
16708                     if(callback){
16709                         callback(false, n);
16710                     }
16711                 }
16712             };
16713             this.expandPath(keys.join(this.pathSeparator), attr, f);
16714         }else{
16715             this.root.select();
16716             if(callback){
16717                 callback(true, this.root);
16718             }
16719         }
16720     },
16721
16722     getTreeEl : function(){
16723         return this.el;
16724     },
16725
16726     /**
16727      * Trigger rendering of this TreePanel
16728      */
16729     render : function(){
16730         if (this.innerCt) {
16731             return this; // stop it rendering more than once!!
16732         }
16733         
16734         this.innerCt = this.el.createChild({tag:"ul",
16735                cls:"x-tree-root-ct " +
16736                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16737
16738         if(this.containerScroll){
16739             Roo.dd.ScrollManager.register(this.el);
16740         }
16741         if((this.enableDD || this.enableDrop) && !this.dropZone){
16742            /**
16743             * The dropZone used by this tree if drop is enabled
16744             * @type Roo.tree.TreeDropZone
16745             */
16746              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16747                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16748            });
16749         }
16750         if((this.enableDD || this.enableDrag) && !this.dragZone){
16751            /**
16752             * The dragZone used by this tree if drag is enabled
16753             * @type Roo.tree.TreeDragZone
16754             */
16755             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16756                ddGroup: this.ddGroup || "TreeDD",
16757                scroll: this.ddScroll
16758            });
16759         }
16760         this.getSelectionModel().init(this);
16761         if (!this.root) {
16762             console.log("ROOT not set in tree");
16763             return;
16764         }
16765         this.root.render();
16766         if(!this.rootVisible){
16767             this.root.renderChildren();
16768         }
16769         return this;
16770     }
16771 });/*
16772  * Based on:
16773  * Ext JS Library 1.1.1
16774  * Copyright(c) 2006-2007, Ext JS, LLC.
16775  *
16776  * Originally Released Under LGPL - original licence link has changed is not relivant.
16777  *
16778  * Fork - LGPL
16779  * <script type="text/javascript">
16780  */
16781  
16782
16783 /**
16784  * @class Roo.tree.DefaultSelectionModel
16785  * @extends Roo.util.Observable
16786  * The default single selection for a TreePanel.
16787  */
16788 Roo.tree.DefaultSelectionModel = function(){
16789    this.selNode = null;
16790    
16791    this.addEvents({
16792        /**
16793         * @event selectionchange
16794         * Fires when the selected node changes
16795         * @param {DefaultSelectionModel} this
16796         * @param {TreeNode} node the new selection
16797         */
16798        "selectionchange" : true,
16799
16800        /**
16801         * @event beforeselect
16802         * Fires before the selected node changes, return false to cancel the change
16803         * @param {DefaultSelectionModel} this
16804         * @param {TreeNode} node the new selection
16805         * @param {TreeNode} node the old selection
16806         */
16807        "beforeselect" : true
16808    });
16809 };
16810
16811 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16812     init : function(tree){
16813         this.tree = tree;
16814         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16815         tree.on("click", this.onNodeClick, this);
16816     },
16817     
16818     onNodeClick : function(node, e){
16819         if (e.ctrlKey && this.selNode == node)  {
16820             this.unselect(node);
16821             return;
16822         }
16823         this.select(node);
16824     },
16825     
16826     /**
16827      * Select a node.
16828      * @param {TreeNode} node The node to select
16829      * @return {TreeNode} The selected node
16830      */
16831     select : function(node){
16832         var last = this.selNode;
16833         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16834             if(last){
16835                 last.ui.onSelectedChange(false);
16836             }
16837             this.selNode = node;
16838             node.ui.onSelectedChange(true);
16839             this.fireEvent("selectionchange", this, node, last);
16840         }
16841         return node;
16842     },
16843     
16844     /**
16845      * Deselect a node.
16846      * @param {TreeNode} node The node to unselect
16847      */
16848     unselect : function(node){
16849         if(this.selNode == node){
16850             this.clearSelections();
16851         }    
16852     },
16853     
16854     /**
16855      * Clear all selections
16856      */
16857     clearSelections : function(){
16858         var n = this.selNode;
16859         if(n){
16860             n.ui.onSelectedChange(false);
16861             this.selNode = null;
16862             this.fireEvent("selectionchange", this, null);
16863         }
16864         return n;
16865     },
16866     
16867     /**
16868      * Get the selected node
16869      * @return {TreeNode} The selected node
16870      */
16871     getSelectedNode : function(){
16872         return this.selNode;    
16873     },
16874     
16875     /**
16876      * Returns true if the node is selected
16877      * @param {TreeNode} node The node to check
16878      * @return {Boolean}
16879      */
16880     isSelected : function(node){
16881         return this.selNode == node;  
16882     },
16883
16884     /**
16885      * Selects the node above the selected node in the tree, intelligently walking the nodes
16886      * @return TreeNode The new selection
16887      */
16888     selectPrevious : function(){
16889         var s = this.selNode || this.lastSelNode;
16890         if(!s){
16891             return null;
16892         }
16893         var ps = s.previousSibling;
16894         if(ps){
16895             if(!ps.isExpanded() || ps.childNodes.length < 1){
16896                 return this.select(ps);
16897             } else{
16898                 var lc = ps.lastChild;
16899                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16900                     lc = lc.lastChild;
16901                 }
16902                 return this.select(lc);
16903             }
16904         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16905             return this.select(s.parentNode);
16906         }
16907         return null;
16908     },
16909
16910     /**
16911      * Selects the node above the selected node in the tree, intelligently walking the nodes
16912      * @return TreeNode The new selection
16913      */
16914     selectNext : function(){
16915         var s = this.selNode || this.lastSelNode;
16916         if(!s){
16917             return null;
16918         }
16919         if(s.firstChild && s.isExpanded()){
16920              return this.select(s.firstChild);
16921          }else if(s.nextSibling){
16922              return this.select(s.nextSibling);
16923          }else if(s.parentNode){
16924             var newS = null;
16925             s.parentNode.bubble(function(){
16926                 if(this.nextSibling){
16927                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16928                     return false;
16929                 }
16930             });
16931             return newS;
16932          }
16933         return null;
16934     },
16935
16936     onKeyDown : function(e){
16937         var s = this.selNode || this.lastSelNode;
16938         // undesirable, but required
16939         var sm = this;
16940         if(!s){
16941             return;
16942         }
16943         var k = e.getKey();
16944         switch(k){
16945              case e.DOWN:
16946                  e.stopEvent();
16947                  this.selectNext();
16948              break;
16949              case e.UP:
16950                  e.stopEvent();
16951                  this.selectPrevious();
16952              break;
16953              case e.RIGHT:
16954                  e.preventDefault();
16955                  if(s.hasChildNodes()){
16956                      if(!s.isExpanded()){
16957                          s.expand();
16958                      }else if(s.firstChild){
16959                          this.select(s.firstChild, e);
16960                      }
16961                  }
16962              break;
16963              case e.LEFT:
16964                  e.preventDefault();
16965                  if(s.hasChildNodes() && s.isExpanded()){
16966                      s.collapse();
16967                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16968                      this.select(s.parentNode, e);
16969                  }
16970              break;
16971         };
16972     }
16973 });
16974
16975 /**
16976  * @class Roo.tree.MultiSelectionModel
16977  * @extends Roo.util.Observable
16978  * Multi selection for a TreePanel.
16979  */
16980 Roo.tree.MultiSelectionModel = function(){
16981    this.selNodes = [];
16982    this.selMap = {};
16983    this.addEvents({
16984        /**
16985         * @event selectionchange
16986         * Fires when the selected nodes change
16987         * @param {MultiSelectionModel} this
16988         * @param {Array} nodes Array of the selected nodes
16989         */
16990        "selectionchange" : true
16991    });
16992 };
16993
16994 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16995     init : function(tree){
16996         this.tree = tree;
16997         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16998         tree.on("click", this.onNodeClick, this);
16999     },
17000     
17001     onNodeClick : function(node, e){
17002         this.select(node, e, e.ctrlKey);
17003     },
17004     
17005     /**
17006      * Select a node.
17007      * @param {TreeNode} node The node to select
17008      * @param {EventObject} e (optional) An event associated with the selection
17009      * @param {Boolean} keepExisting True to retain existing selections
17010      * @return {TreeNode} The selected node
17011      */
17012     select : function(node, e, keepExisting){
17013         if(keepExisting !== true){
17014             this.clearSelections(true);
17015         }
17016         if(this.isSelected(node)){
17017             this.lastSelNode = node;
17018             return node;
17019         }
17020         this.selNodes.push(node);
17021         this.selMap[node.id] = node;
17022         this.lastSelNode = node;
17023         node.ui.onSelectedChange(true);
17024         this.fireEvent("selectionchange", this, this.selNodes);
17025         return node;
17026     },
17027     
17028     /**
17029      * Deselect a node.
17030      * @param {TreeNode} node The node to unselect
17031      */
17032     unselect : function(node){
17033         if(this.selMap[node.id]){
17034             node.ui.onSelectedChange(false);
17035             var sn = this.selNodes;
17036             var index = -1;
17037             if(sn.indexOf){
17038                 index = sn.indexOf(node);
17039             }else{
17040                 for(var i = 0, len = sn.length; i < len; i++){
17041                     if(sn[i] == node){
17042                         index = i;
17043                         break;
17044                     }
17045                 }
17046             }
17047             if(index != -1){
17048                 this.selNodes.splice(index, 1);
17049             }
17050             delete this.selMap[node.id];
17051             this.fireEvent("selectionchange", this, this.selNodes);
17052         }
17053     },
17054     
17055     /**
17056      * Clear all selections
17057      */
17058     clearSelections : function(suppressEvent){
17059         var sn = this.selNodes;
17060         if(sn.length > 0){
17061             for(var i = 0, len = sn.length; i < len; i++){
17062                 sn[i].ui.onSelectedChange(false);
17063             }
17064             this.selNodes = [];
17065             this.selMap = {};
17066             if(suppressEvent !== true){
17067                 this.fireEvent("selectionchange", this, this.selNodes);
17068             }
17069         }
17070     },
17071     
17072     /**
17073      * Returns true if the node is selected
17074      * @param {TreeNode} node The node to check
17075      * @return {Boolean}
17076      */
17077     isSelected : function(node){
17078         return this.selMap[node.id] ? true : false;  
17079     },
17080     
17081     /**
17082      * Returns an array of the selected nodes
17083      * @return {Array}
17084      */
17085     getSelectedNodes : function(){
17086         return this.selNodes;    
17087     },
17088
17089     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17090
17091     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17092
17093     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17094 });/*
17095  * Based on:
17096  * Ext JS Library 1.1.1
17097  * Copyright(c) 2006-2007, Ext JS, LLC.
17098  *
17099  * Originally Released Under LGPL - original licence link has changed is not relivant.
17100  *
17101  * Fork - LGPL
17102  * <script type="text/javascript">
17103  */
17104  
17105 /**
17106  * @class Roo.tree.TreeNode
17107  * @extends Roo.data.Node
17108  * @cfg {String} text The text for this node
17109  * @cfg {Boolean} expanded true to start the node expanded
17110  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17111  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17112  * @cfg {Boolean} disabled true to start the node disabled
17113  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17114  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17115  * @cfg {String} cls A css class to be added to the node
17116  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17117  * @cfg {String} href URL of the link used for the node (defaults to #)
17118  * @cfg {String} hrefTarget target frame for the link
17119  * @cfg {String} qtip An Ext QuickTip for the node
17120  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17121  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17122  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17123  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17124  * (defaults to undefined with no checkbox rendered)
17125  * @constructor
17126  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17127  */
17128 Roo.tree.TreeNode = function(attributes){
17129     attributes = attributes || {};
17130     if(typeof attributes == "string"){
17131         attributes = {text: attributes};
17132     }
17133     this.childrenRendered = false;
17134     this.rendered = false;
17135     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17136     this.expanded = attributes.expanded === true;
17137     this.isTarget = attributes.isTarget !== false;
17138     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17139     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17140
17141     /**
17142      * Read-only. The text for this node. To change it use setText().
17143      * @type String
17144      */
17145     this.text = attributes.text;
17146     /**
17147      * True if this node is disabled.
17148      * @type Boolean
17149      */
17150     this.disabled = attributes.disabled === true;
17151
17152     this.addEvents({
17153         /**
17154         * @event textchange
17155         * Fires when the text for this node is changed
17156         * @param {Node} this This node
17157         * @param {String} text The new text
17158         * @param {String} oldText The old text
17159         */
17160         "textchange" : true,
17161         /**
17162         * @event beforeexpand
17163         * Fires before this node is expanded, return false to cancel.
17164         * @param {Node} this This node
17165         * @param {Boolean} deep
17166         * @param {Boolean} anim
17167         */
17168         "beforeexpand" : true,
17169         /**
17170         * @event beforecollapse
17171         * Fires before this node is collapsed, return false to cancel.
17172         * @param {Node} this This node
17173         * @param {Boolean} deep
17174         * @param {Boolean} anim
17175         */
17176         "beforecollapse" : true,
17177         /**
17178         * @event expand
17179         * Fires when this node is expanded
17180         * @param {Node} this This node
17181         */
17182         "expand" : true,
17183         /**
17184         * @event disabledchange
17185         * Fires when the disabled status of this node changes
17186         * @param {Node} this This node
17187         * @param {Boolean} disabled
17188         */
17189         "disabledchange" : true,
17190         /**
17191         * @event collapse
17192         * Fires when this node is collapsed
17193         * @param {Node} this This node
17194         */
17195         "collapse" : true,
17196         /**
17197         * @event beforeclick
17198         * Fires before click processing. Return false to cancel the default action.
17199         * @param {Node} this This node
17200         * @param {Roo.EventObject} e The event object
17201         */
17202         "beforeclick":true,
17203         /**
17204         * @event checkchange
17205         * Fires when a node with a checkbox's checked property changes
17206         * @param {Node} this This node
17207         * @param {Boolean} checked
17208         */
17209         "checkchange":true,
17210         /**
17211         * @event click
17212         * Fires when this node is clicked
17213         * @param {Node} this This node
17214         * @param {Roo.EventObject} e The event object
17215         */
17216         "click":true,
17217         /**
17218         * @event dblclick
17219         * Fires when this node is double clicked
17220         * @param {Node} this This node
17221         * @param {Roo.EventObject} e The event object
17222         */
17223         "dblclick":true,
17224         /**
17225         * @event contextmenu
17226         * Fires when this node is right clicked
17227         * @param {Node} this This node
17228         * @param {Roo.EventObject} e The event object
17229         */
17230         "contextmenu":true,
17231         /**
17232         * @event beforechildrenrendered
17233         * Fires right before the child nodes for this node are rendered
17234         * @param {Node} this This node
17235         */
17236         "beforechildrenrendered":true
17237     });
17238
17239     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17240
17241     /**
17242      * Read-only. The UI for this node
17243      * @type TreeNodeUI
17244      */
17245     this.ui = new uiClass(this);
17246 };
17247 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17248     preventHScroll: true,
17249     /**
17250      * Returns true if this node is expanded
17251      * @return {Boolean}
17252      */
17253     isExpanded : function(){
17254         return this.expanded;
17255     },
17256
17257     /**
17258      * Returns the UI object for this node
17259      * @return {TreeNodeUI}
17260      */
17261     getUI : function(){
17262         return this.ui;
17263     },
17264
17265     // private override
17266     setFirstChild : function(node){
17267         var of = this.firstChild;
17268         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17269         if(this.childrenRendered && of && node != of){
17270             of.renderIndent(true, true);
17271         }
17272         if(this.rendered){
17273             this.renderIndent(true, true);
17274         }
17275     },
17276
17277     // private override
17278     setLastChild : function(node){
17279         var ol = this.lastChild;
17280         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17281         if(this.childrenRendered && ol && node != ol){
17282             ol.renderIndent(true, true);
17283         }
17284         if(this.rendered){
17285             this.renderIndent(true, true);
17286         }
17287     },
17288
17289     // these methods are overridden to provide lazy rendering support
17290     // private override
17291     appendChild : function(){
17292         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17293         if(node && this.childrenRendered){
17294             node.render();
17295         }
17296         this.ui.updateExpandIcon();
17297         return node;
17298     },
17299
17300     // private override
17301     removeChild : function(node){
17302         this.ownerTree.getSelectionModel().unselect(node);
17303         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17304         // if it's been rendered remove dom node
17305         if(this.childrenRendered){
17306             node.ui.remove();
17307         }
17308         if(this.childNodes.length < 1){
17309             this.collapse(false, false);
17310         }else{
17311             this.ui.updateExpandIcon();
17312         }
17313         if(!this.firstChild) {
17314             this.childrenRendered = false;
17315         }
17316         return node;
17317     },
17318
17319     // private override
17320     insertBefore : function(node, refNode){
17321         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17322         if(newNode && refNode && this.childrenRendered){
17323             node.render();
17324         }
17325         this.ui.updateExpandIcon();
17326         return newNode;
17327     },
17328
17329     /**
17330      * Sets the text for this node
17331      * @param {String} text
17332      */
17333     setText : function(text){
17334         var oldText = this.text;
17335         this.text = text;
17336         this.attributes.text = text;
17337         if(this.rendered){ // event without subscribing
17338             this.ui.onTextChange(this, text, oldText);
17339         }
17340         this.fireEvent("textchange", this, text, oldText);
17341     },
17342
17343     /**
17344      * Triggers selection of this node
17345      */
17346     select : function(){
17347         this.getOwnerTree().getSelectionModel().select(this);
17348     },
17349
17350     /**
17351      * Triggers deselection of this node
17352      */
17353     unselect : function(){
17354         this.getOwnerTree().getSelectionModel().unselect(this);
17355     },
17356
17357     /**
17358      * Returns true if this node is selected
17359      * @return {Boolean}
17360      */
17361     isSelected : function(){
17362         return this.getOwnerTree().getSelectionModel().isSelected(this);
17363     },
17364
17365     /**
17366      * Expand this node.
17367      * @param {Boolean} deep (optional) True to expand all children as well
17368      * @param {Boolean} anim (optional) false to cancel the default animation
17369      * @param {Function} callback (optional) A callback to be called when
17370      * expanding this node completes (does not wait for deep expand to complete).
17371      * Called with 1 parameter, this node.
17372      */
17373     expand : function(deep, anim, callback){
17374         if(!this.expanded){
17375             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17376                 return;
17377             }
17378             if(!this.childrenRendered){
17379                 this.renderChildren();
17380             }
17381             this.expanded = true;
17382             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17383                 this.ui.animExpand(function(){
17384                     this.fireEvent("expand", this);
17385                     if(typeof callback == "function"){
17386                         callback(this);
17387                     }
17388                     if(deep === true){
17389                         this.expandChildNodes(true);
17390                     }
17391                 }.createDelegate(this));
17392                 return;
17393             }else{
17394                 this.ui.expand();
17395                 this.fireEvent("expand", this);
17396                 if(typeof callback == "function"){
17397                     callback(this);
17398                 }
17399             }
17400         }else{
17401            if(typeof callback == "function"){
17402                callback(this);
17403            }
17404         }
17405         if(deep === true){
17406             this.expandChildNodes(true);
17407         }
17408     },
17409
17410     isHiddenRoot : function(){
17411         return this.isRoot && !this.getOwnerTree().rootVisible;
17412     },
17413
17414     /**
17415      * Collapse this node.
17416      * @param {Boolean} deep (optional) True to collapse all children as well
17417      * @param {Boolean} anim (optional) false to cancel the default animation
17418      */
17419     collapse : function(deep, anim){
17420         if(this.expanded && !this.isHiddenRoot()){
17421             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17422                 return;
17423             }
17424             this.expanded = false;
17425             if((this.getOwnerTree().animate && anim !== false) || anim){
17426                 this.ui.animCollapse(function(){
17427                     this.fireEvent("collapse", this);
17428                     if(deep === true){
17429                         this.collapseChildNodes(true);
17430                     }
17431                 }.createDelegate(this));
17432                 return;
17433             }else{
17434                 this.ui.collapse();
17435                 this.fireEvent("collapse", this);
17436             }
17437         }
17438         if(deep === true){
17439             var cs = this.childNodes;
17440             for(var i = 0, len = cs.length; i < len; i++) {
17441                 cs[i].collapse(true, false);
17442             }
17443         }
17444     },
17445
17446     // private
17447     delayedExpand : function(delay){
17448         if(!this.expandProcId){
17449             this.expandProcId = this.expand.defer(delay, this);
17450         }
17451     },
17452
17453     // private
17454     cancelExpand : function(){
17455         if(this.expandProcId){
17456             clearTimeout(this.expandProcId);
17457         }
17458         this.expandProcId = false;
17459     },
17460
17461     /**
17462      * Toggles expanded/collapsed state of the node
17463      */
17464     toggle : function(){
17465         if(this.expanded){
17466             this.collapse();
17467         }else{
17468             this.expand();
17469         }
17470     },
17471
17472     /**
17473      * Ensures all parent nodes are expanded
17474      */
17475     ensureVisible : function(callback){
17476         var tree = this.getOwnerTree();
17477         tree.expandPath(this.parentNode.getPath(), false, function(){
17478             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17479             Roo.callback(callback);
17480         }.createDelegate(this));
17481     },
17482
17483     /**
17484      * Expand all child nodes
17485      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17486      */
17487     expandChildNodes : function(deep){
17488         var cs = this.childNodes;
17489         for(var i = 0, len = cs.length; i < len; i++) {
17490                 cs[i].expand(deep);
17491         }
17492     },
17493
17494     /**
17495      * Collapse all child nodes
17496      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17497      */
17498     collapseChildNodes : function(deep){
17499         var cs = this.childNodes;
17500         for(var i = 0, len = cs.length; i < len; i++) {
17501                 cs[i].collapse(deep);
17502         }
17503     },
17504
17505     /**
17506      * Disables this node
17507      */
17508     disable : function(){
17509         this.disabled = true;
17510         this.unselect();
17511         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17512             this.ui.onDisableChange(this, true);
17513         }
17514         this.fireEvent("disabledchange", this, true);
17515     },
17516
17517     /**
17518      * Enables this node
17519      */
17520     enable : function(){
17521         this.disabled = false;
17522         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17523             this.ui.onDisableChange(this, false);
17524         }
17525         this.fireEvent("disabledchange", this, false);
17526     },
17527
17528     // private
17529     renderChildren : function(suppressEvent){
17530         if(suppressEvent !== false){
17531             this.fireEvent("beforechildrenrendered", this);
17532         }
17533         var cs = this.childNodes;
17534         for(var i = 0, len = cs.length; i < len; i++){
17535             cs[i].render(true);
17536         }
17537         this.childrenRendered = true;
17538     },
17539
17540     // private
17541     sort : function(fn, scope){
17542         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17543         if(this.childrenRendered){
17544             var cs = this.childNodes;
17545             for(var i = 0, len = cs.length; i < len; i++){
17546                 cs[i].render(true);
17547             }
17548         }
17549     },
17550
17551     // private
17552     render : function(bulkRender){
17553         this.ui.render(bulkRender);
17554         if(!this.rendered){
17555             this.rendered = true;
17556             if(this.expanded){
17557                 this.expanded = false;
17558                 this.expand(false, false);
17559             }
17560         }
17561     },
17562
17563     // private
17564     renderIndent : function(deep, refresh){
17565         if(refresh){
17566             this.ui.childIndent = null;
17567         }
17568         this.ui.renderIndent();
17569         if(deep === true && this.childrenRendered){
17570             var cs = this.childNodes;
17571             for(var i = 0, len = cs.length; i < len; i++){
17572                 cs[i].renderIndent(true, refresh);
17573             }
17574         }
17575     }
17576 });/*
17577  * Based on:
17578  * Ext JS Library 1.1.1
17579  * Copyright(c) 2006-2007, Ext JS, LLC.
17580  *
17581  * Originally Released Under LGPL - original licence link has changed is not relivant.
17582  *
17583  * Fork - LGPL
17584  * <script type="text/javascript">
17585  */
17586  
17587 /**
17588  * @class Roo.tree.AsyncTreeNode
17589  * @extends Roo.tree.TreeNode
17590  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17591  * @constructor
17592  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17593  */
17594  Roo.tree.AsyncTreeNode = function(config){
17595     this.loaded = false;
17596     this.loading = false;
17597     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17598     /**
17599     * @event beforeload
17600     * Fires before this node is loaded, return false to cancel
17601     * @param {Node} this This node
17602     */
17603     this.addEvents({'beforeload':true, 'load': true});
17604     /**
17605     * @event load
17606     * Fires when this node is loaded
17607     * @param {Node} this This node
17608     */
17609     /**
17610      * The loader used by this node (defaults to using the tree's defined loader)
17611      * @type TreeLoader
17612      * @property loader
17613      */
17614 };
17615 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17616     expand : function(deep, anim, callback){
17617         if(this.loading){ // if an async load is already running, waiting til it's done
17618             var timer;
17619             var f = function(){
17620                 if(!this.loading){ // done loading
17621                     clearInterval(timer);
17622                     this.expand(deep, anim, callback);
17623                 }
17624             }.createDelegate(this);
17625             timer = setInterval(f, 200);
17626             return;
17627         }
17628         if(!this.loaded){
17629             if(this.fireEvent("beforeload", this) === false){
17630                 return;
17631             }
17632             this.loading = true;
17633             this.ui.beforeLoad(this);
17634             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17635             if(loader){
17636                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17637                 return;
17638             }
17639         }
17640         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17641     },
17642     
17643     /**
17644      * Returns true if this node is currently loading
17645      * @return {Boolean}
17646      */
17647     isLoading : function(){
17648         return this.loading;  
17649     },
17650     
17651     loadComplete : function(deep, anim, callback){
17652         this.loading = false;
17653         this.loaded = true;
17654         this.ui.afterLoad(this);
17655         this.fireEvent("load", this);
17656         this.expand(deep, anim, callback);
17657     },
17658     
17659     /**
17660      * Returns true if this node has been loaded
17661      * @return {Boolean}
17662      */
17663     isLoaded : function(){
17664         return this.loaded;
17665     },
17666     
17667     hasChildNodes : function(){
17668         if(!this.isLeaf() && !this.loaded){
17669             return true;
17670         }else{
17671             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17672         }
17673     },
17674
17675     /**
17676      * Trigger a reload for this node
17677      * @param {Function} callback
17678      */
17679     reload : function(callback){
17680         this.collapse(false, false);
17681         while(this.firstChild){
17682             this.removeChild(this.firstChild);
17683         }
17684         this.childrenRendered = false;
17685         this.loaded = false;
17686         if(this.isHiddenRoot()){
17687             this.expanded = false;
17688         }
17689         this.expand(false, false, callback);
17690     }
17691 });/*
17692  * Based on:
17693  * Ext JS Library 1.1.1
17694  * Copyright(c) 2006-2007, Ext JS, LLC.
17695  *
17696  * Originally Released Under LGPL - original licence link has changed is not relivant.
17697  *
17698  * Fork - LGPL
17699  * <script type="text/javascript">
17700  */
17701  
17702 /**
17703  * @class Roo.tree.TreeNodeUI
17704  * @constructor
17705  * @param {Object} node The node to render
17706  * The TreeNode UI implementation is separate from the
17707  * tree implementation. Unless you are customizing the tree UI,
17708  * you should never have to use this directly.
17709  */
17710 Roo.tree.TreeNodeUI = function(node){
17711     this.node = node;
17712     this.rendered = false;
17713     this.animating = false;
17714     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17715 };
17716
17717 Roo.tree.TreeNodeUI.prototype = {
17718     removeChild : function(node){
17719         if(this.rendered){
17720             this.ctNode.removeChild(node.ui.getEl());
17721         }
17722     },
17723
17724     beforeLoad : function(){
17725          this.addClass("x-tree-node-loading");
17726     },
17727
17728     afterLoad : function(){
17729          this.removeClass("x-tree-node-loading");
17730     },
17731
17732     onTextChange : function(node, text, oldText){
17733         if(this.rendered){
17734             this.textNode.innerHTML = text;
17735         }
17736     },
17737
17738     onDisableChange : function(node, state){
17739         this.disabled = state;
17740         if(state){
17741             this.addClass("x-tree-node-disabled");
17742         }else{
17743             this.removeClass("x-tree-node-disabled");
17744         }
17745     },
17746
17747     onSelectedChange : function(state){
17748         if(state){
17749             this.focus();
17750             this.addClass("x-tree-selected");
17751         }else{
17752             //this.blur();
17753             this.removeClass("x-tree-selected");
17754         }
17755     },
17756
17757     onMove : function(tree, node, oldParent, newParent, index, refNode){
17758         this.childIndent = null;
17759         if(this.rendered){
17760             var targetNode = newParent.ui.getContainer();
17761             if(!targetNode){//target not rendered
17762                 this.holder = document.createElement("div");
17763                 this.holder.appendChild(this.wrap);
17764                 return;
17765             }
17766             var insertBefore = refNode ? refNode.ui.getEl() : null;
17767             if(insertBefore){
17768                 targetNode.insertBefore(this.wrap, insertBefore);
17769             }else{
17770                 targetNode.appendChild(this.wrap);
17771             }
17772             this.node.renderIndent(true);
17773         }
17774     },
17775
17776     addClass : function(cls){
17777         if(this.elNode){
17778             Roo.fly(this.elNode).addClass(cls);
17779         }
17780     },
17781
17782     removeClass : function(cls){
17783         if(this.elNode){
17784             Roo.fly(this.elNode).removeClass(cls);
17785         }
17786     },
17787
17788     remove : function(){
17789         if(this.rendered){
17790             this.holder = document.createElement("div");
17791             this.holder.appendChild(this.wrap);
17792         }
17793     },
17794
17795     fireEvent : function(){
17796         return this.node.fireEvent.apply(this.node, arguments);
17797     },
17798
17799     initEvents : function(){
17800         this.node.on("move", this.onMove, this);
17801         var E = Roo.EventManager;
17802         var a = this.anchor;
17803
17804         var el = Roo.fly(a, '_treeui');
17805
17806         if(Roo.isOpera){ // opera render bug ignores the CSS
17807             el.setStyle("text-decoration", "none");
17808         }
17809
17810         el.on("click", this.onClick, this);
17811         el.on("dblclick", this.onDblClick, this);
17812
17813         if(this.checkbox){
17814             Roo.EventManager.on(this.checkbox,
17815                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17816         }
17817
17818         el.on("contextmenu", this.onContextMenu, this);
17819
17820         var icon = Roo.fly(this.iconNode);
17821         icon.on("click", this.onClick, this);
17822         icon.on("dblclick", this.onDblClick, this);
17823         icon.on("contextmenu", this.onContextMenu, this);
17824         E.on(this.ecNode, "click", this.ecClick, this, true);
17825
17826         if(this.node.disabled){
17827             this.addClass("x-tree-node-disabled");
17828         }
17829         if(this.node.hidden){
17830             this.addClass("x-tree-node-disabled");
17831         }
17832         var ot = this.node.getOwnerTree();
17833         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17834         if(dd && (!this.node.isRoot || ot.rootVisible)){
17835             Roo.dd.Registry.register(this.elNode, {
17836                 node: this.node,
17837                 handles: this.getDDHandles(),
17838                 isHandle: false
17839             });
17840         }
17841     },
17842
17843     getDDHandles : function(){
17844         return [this.iconNode, this.textNode];
17845     },
17846
17847     hide : function(){
17848         if(this.rendered){
17849             this.wrap.style.display = "none";
17850         }
17851     },
17852
17853     show : function(){
17854         if(this.rendered){
17855             this.wrap.style.display = "";
17856         }
17857     },
17858
17859     onContextMenu : function(e){
17860         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17861             e.preventDefault();
17862             this.focus();
17863             this.fireEvent("contextmenu", this.node, e);
17864         }
17865     },
17866
17867     onClick : function(e){
17868         if(this.dropping){
17869             e.stopEvent();
17870             return;
17871         }
17872         if(this.fireEvent("beforeclick", this.node, e) !== false){
17873             if(!this.disabled && this.node.attributes.href){
17874                 this.fireEvent("click", this.node, e);
17875                 return;
17876             }
17877             e.preventDefault();
17878             if(this.disabled){
17879                 return;
17880             }
17881
17882             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17883                 this.node.toggle();
17884             }
17885
17886             this.fireEvent("click", this.node, e);
17887         }else{
17888             e.stopEvent();
17889         }
17890     },
17891
17892     onDblClick : function(e){
17893         e.preventDefault();
17894         if(this.disabled){
17895             return;
17896         }
17897         if(this.checkbox){
17898             this.toggleCheck();
17899         }
17900         if(!this.animating && this.node.hasChildNodes()){
17901             this.node.toggle();
17902         }
17903         this.fireEvent("dblclick", this.node, e);
17904     },
17905
17906     onCheckChange : function(){
17907         var checked = this.checkbox.checked;
17908         this.node.attributes.checked = checked;
17909         this.fireEvent('checkchange', this.node, checked);
17910     },
17911
17912     ecClick : function(e){
17913         if(!this.animating && this.node.hasChildNodes()){
17914             this.node.toggle();
17915         }
17916     },
17917
17918     startDrop : function(){
17919         this.dropping = true;
17920     },
17921
17922     // delayed drop so the click event doesn't get fired on a drop
17923     endDrop : function(){
17924        setTimeout(function(){
17925            this.dropping = false;
17926        }.createDelegate(this), 50);
17927     },
17928
17929     expand : function(){
17930         this.updateExpandIcon();
17931         this.ctNode.style.display = "";
17932     },
17933
17934     focus : function(){
17935         if(!this.node.preventHScroll){
17936             try{this.anchor.focus();
17937             }catch(e){}
17938         }else if(!Roo.isIE){
17939             try{
17940                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17941                 var l = noscroll.scrollLeft;
17942                 this.anchor.focus();
17943                 noscroll.scrollLeft = l;
17944             }catch(e){}
17945         }
17946     },
17947
17948     toggleCheck : function(value){
17949         var cb = this.checkbox;
17950         if(cb){
17951             cb.checked = (value === undefined ? !cb.checked : value);
17952         }
17953     },
17954
17955     blur : function(){
17956         try{
17957             this.anchor.blur();
17958         }catch(e){}
17959     },
17960
17961     animExpand : function(callback){
17962         var ct = Roo.get(this.ctNode);
17963         ct.stopFx();
17964         if(!this.node.hasChildNodes()){
17965             this.updateExpandIcon();
17966             this.ctNode.style.display = "";
17967             Roo.callback(callback);
17968             return;
17969         }
17970         this.animating = true;
17971         this.updateExpandIcon();
17972
17973         ct.slideIn('t', {
17974            callback : function(){
17975                this.animating = false;
17976                Roo.callback(callback);
17977             },
17978             scope: this,
17979             duration: this.node.ownerTree.duration || .25
17980         });
17981     },
17982
17983     highlight : function(){
17984         var tree = this.node.getOwnerTree();
17985         Roo.fly(this.wrap).highlight(
17986             tree.hlColor || "C3DAF9",
17987             {endColor: tree.hlBaseColor}
17988         );
17989     },
17990
17991     collapse : function(){
17992         this.updateExpandIcon();
17993         this.ctNode.style.display = "none";
17994     },
17995
17996     animCollapse : function(callback){
17997         var ct = Roo.get(this.ctNode);
17998         ct.enableDisplayMode('block');
17999         ct.stopFx();
18000
18001         this.animating = true;
18002         this.updateExpandIcon();
18003
18004         ct.slideOut('t', {
18005             callback : function(){
18006                this.animating = false;
18007                Roo.callback(callback);
18008             },
18009             scope: this,
18010             duration: this.node.ownerTree.duration || .25
18011         });
18012     },
18013
18014     getContainer : function(){
18015         return this.ctNode;
18016     },
18017
18018     getEl : function(){
18019         return this.wrap;
18020     },
18021
18022     appendDDGhost : function(ghostNode){
18023         ghostNode.appendChild(this.elNode.cloneNode(true));
18024     },
18025
18026     getDDRepairXY : function(){
18027         return Roo.lib.Dom.getXY(this.iconNode);
18028     },
18029
18030     onRender : function(){
18031         this.render();
18032     },
18033
18034     render : function(bulkRender){
18035         var n = this.node, a = n.attributes;
18036         var targetNode = n.parentNode ?
18037               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18038
18039         if(!this.rendered){
18040             this.rendered = true;
18041
18042             this.renderElements(n, a, targetNode, bulkRender);
18043
18044             if(a.qtip){
18045                if(this.textNode.setAttributeNS){
18046                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18047                    if(a.qtipTitle){
18048                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18049                    }
18050                }else{
18051                    this.textNode.setAttribute("ext:qtip", a.qtip);
18052                    if(a.qtipTitle){
18053                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18054                    }
18055                }
18056             }else if(a.qtipCfg){
18057                 a.qtipCfg.target = Roo.id(this.textNode);
18058                 Roo.QuickTips.register(a.qtipCfg);
18059             }
18060             this.initEvents();
18061             if(!this.node.expanded){
18062                 this.updateExpandIcon();
18063             }
18064         }else{
18065             if(bulkRender === true) {
18066                 targetNode.appendChild(this.wrap);
18067             }
18068         }
18069     },
18070
18071     renderElements : function(n, a, targetNode, bulkRender){
18072         // add some indent caching, this helps performance when rendering a large tree
18073         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18074         var t = n.getOwnerTree();
18075         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18076         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18077         var cb = typeof a.checked == 'boolean';
18078         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18079         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18080             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18081             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18082             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18083             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18084             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18085              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18086                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18087             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18088             "</li>"];
18089
18090         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18091             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18092                                 n.nextSibling.ui.getEl(), buf.join(""));
18093         }else{
18094             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18095         }
18096
18097         this.elNode = this.wrap.childNodes[0];
18098         this.ctNode = this.wrap.childNodes[1];
18099         var cs = this.elNode.childNodes;
18100         this.indentNode = cs[0];
18101         this.ecNode = cs[1];
18102         this.iconNode = cs[2];
18103         var index = 3;
18104         if(cb){
18105             this.checkbox = cs[3];
18106             index++;
18107         }
18108         this.anchor = cs[index];
18109         this.textNode = cs[index].firstChild;
18110     },
18111
18112     getAnchor : function(){
18113         return this.anchor;
18114     },
18115
18116     getTextEl : function(){
18117         return this.textNode;
18118     },
18119
18120     getIconEl : function(){
18121         return this.iconNode;
18122     },
18123
18124     isChecked : function(){
18125         return this.checkbox ? this.checkbox.checked : false;
18126     },
18127
18128     updateExpandIcon : function(){
18129         if(this.rendered){
18130             var n = this.node, c1, c2;
18131             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18132             var hasChild = n.hasChildNodes();
18133             if(hasChild){
18134                 if(n.expanded){
18135                     cls += "-minus";
18136                     c1 = "x-tree-node-collapsed";
18137                     c2 = "x-tree-node-expanded";
18138                 }else{
18139                     cls += "-plus";
18140                     c1 = "x-tree-node-expanded";
18141                     c2 = "x-tree-node-collapsed";
18142                 }
18143                 if(this.wasLeaf){
18144                     this.removeClass("x-tree-node-leaf");
18145                     this.wasLeaf = false;
18146                 }
18147                 if(this.c1 != c1 || this.c2 != c2){
18148                     Roo.fly(this.elNode).replaceClass(c1, c2);
18149                     this.c1 = c1; this.c2 = c2;
18150                 }
18151             }else{
18152                 if(!this.wasLeaf){
18153                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18154                     delete this.c1;
18155                     delete this.c2;
18156                     this.wasLeaf = true;
18157                 }
18158             }
18159             var ecc = "x-tree-ec-icon "+cls;
18160             if(this.ecc != ecc){
18161                 this.ecNode.className = ecc;
18162                 this.ecc = ecc;
18163             }
18164         }
18165     },
18166
18167     getChildIndent : function(){
18168         if(!this.childIndent){
18169             var buf = [];
18170             var p = this.node;
18171             while(p){
18172                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18173                     if(!p.isLast()) {
18174                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18175                     } else {
18176                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18177                     }
18178                 }
18179                 p = p.parentNode;
18180             }
18181             this.childIndent = buf.join("");
18182         }
18183         return this.childIndent;
18184     },
18185
18186     renderIndent : function(){
18187         if(this.rendered){
18188             var indent = "";
18189             var p = this.node.parentNode;
18190             if(p){
18191                 indent = p.ui.getChildIndent();
18192             }
18193             if(this.indentMarkup != indent){ // don't rerender if not required
18194                 this.indentNode.innerHTML = indent;
18195                 this.indentMarkup = indent;
18196             }
18197             this.updateExpandIcon();
18198         }
18199     }
18200 };
18201
18202 Roo.tree.RootTreeNodeUI = function(){
18203     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18204 };
18205 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18206     render : function(){
18207         if(!this.rendered){
18208             var targetNode = this.node.ownerTree.innerCt.dom;
18209             this.node.expanded = true;
18210             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18211             this.wrap = this.ctNode = targetNode.firstChild;
18212         }
18213     },
18214     collapse : function(){
18215     },
18216     expand : function(){
18217     }
18218 });/*
18219  * Based on:
18220  * Ext JS Library 1.1.1
18221  * Copyright(c) 2006-2007, Ext JS, LLC.
18222  *
18223  * Originally Released Under LGPL - original licence link has changed is not relivant.
18224  *
18225  * Fork - LGPL
18226  * <script type="text/javascript">
18227  */
18228 /**
18229  * @class Roo.tree.TreeLoader
18230  * @extends Roo.util.Observable
18231  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18232  * nodes from a specified URL. The response must be a javascript Array definition
18233  * who's elements are node definition objects. eg:
18234  * <pre><code>
18235    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18236     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18237 </code></pre>
18238  * <br><br>
18239  * A server request is sent, and child nodes are loaded only when a node is expanded.
18240  * The loading node's id is passed to the server under the parameter name "node" to
18241  * enable the server to produce the correct child nodes.
18242  * <br><br>
18243  * To pass extra parameters, an event handler may be attached to the "beforeload"
18244  * event, and the parameters specified in the TreeLoader's baseParams property:
18245  * <pre><code>
18246     myTreeLoader.on("beforeload", function(treeLoader, node) {
18247         this.baseParams.category = node.attributes.category;
18248     }, this);
18249 </code></pre><
18250  * This would pass an HTTP parameter called "category" to the server containing
18251  * the value of the Node's "category" attribute.
18252  * @constructor
18253  * Creates a new Treeloader.
18254  * @param {Object} config A config object containing config properties.
18255  */
18256 Roo.tree.TreeLoader = function(config){
18257     this.baseParams = {};
18258     this.requestMethod = "POST";
18259     Roo.apply(this, config);
18260
18261     this.addEvents({
18262     
18263         /**
18264          * @event beforeload
18265          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18266          * @param {Object} This TreeLoader object.
18267          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18268          * @param {Object} callback The callback function specified in the {@link #load} call.
18269          */
18270         beforeload : true,
18271         /**
18272          * @event load
18273          * Fires when the node has been successfuly loaded.
18274          * @param {Object} This TreeLoader object.
18275          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18276          * @param {Object} response The response object containing the data from the server.
18277          */
18278         load : true,
18279         /**
18280          * @event loadexception
18281          * Fires if the network request failed.
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         loadexception : true,
18287         /**
18288          * @event create
18289          * Fires before a node is created, enabling you to return custom Node types 
18290          * @param {Object} This TreeLoader object.
18291          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18292          */
18293         create : true
18294     });
18295
18296     Roo.tree.TreeLoader.superclass.constructor.call(this);
18297 };
18298
18299 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18300     /**
18301     * @cfg {String} dataUrl The URL from which to request a Json string which
18302     * specifies an array of node definition object representing the child nodes
18303     * to be loaded.
18304     */
18305     /**
18306     * @cfg {Object} baseParams (optional) An object containing properties which
18307     * specify HTTP parameters to be passed to each request for child nodes.
18308     */
18309     /**
18310     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18311     * created by this loader. If the attributes sent by the server have an attribute in this object,
18312     * they take priority.
18313     */
18314     /**
18315     * @cfg {Object} uiProviders (optional) An object containing properties which
18316     * 
18317     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18318     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18319     * <i>uiProvider</i> attribute of a returned child node is a string rather
18320     * than a reference to a TreeNodeUI implementation, this that string value
18321     * is used as a property name in the uiProviders object. You can define the provider named
18322     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18323     */
18324     uiProviders : {},
18325
18326     /**
18327     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18328     * child nodes before loading.
18329     */
18330     clearOnLoad : true,
18331
18332     /**
18333     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18334     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18335     * Grid query { data : [ .....] }
18336     */
18337     
18338     root : false,
18339      /**
18340     * @cfg {String} queryParam (optional) 
18341     * Name of the query as it will be passed on the querystring (defaults to 'node')
18342     * eg. the request will be ?node=[id]
18343     */
18344     
18345     
18346     queryParam: false,
18347     
18348     /**
18349      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18350      * This is called automatically when a node is expanded, but may be used to reload
18351      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18352      * @param {Roo.tree.TreeNode} node
18353      * @param {Function} callback
18354      */
18355     load : function(node, callback){
18356         if(this.clearOnLoad){
18357             while(node.firstChild){
18358                 node.removeChild(node.firstChild);
18359             }
18360         }
18361         if(node.attributes.children){ // preloaded json children
18362             var cs = node.attributes.children;
18363             for(var i = 0, len = cs.length; i < len; i++){
18364                 node.appendChild(this.createNode(cs[i]));
18365             }
18366             if(typeof callback == "function"){
18367                 callback();
18368             }
18369         }else if(this.dataUrl){
18370             this.requestData(node, callback);
18371         }
18372     },
18373
18374     getParams: function(node){
18375         var buf = [], bp = this.baseParams;
18376         for(var key in bp){
18377             if(typeof bp[key] != "function"){
18378                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18379             }
18380         }
18381         var n = this.queryParam === false ? 'node' : this.queryParam;
18382         buf.push(n + "=", encodeURIComponent(node.id));
18383         return buf.join("");
18384     },
18385
18386     requestData : function(node, callback){
18387         if(this.fireEvent("beforeload", this, node, callback) !== false){
18388             this.transId = Roo.Ajax.request({
18389                 method:this.requestMethod,
18390                 url: this.dataUrl||this.url,
18391                 success: this.handleResponse,
18392                 failure: this.handleFailure,
18393                 scope: this,
18394                 argument: {callback: callback, node: node},
18395                 params: this.getParams(node)
18396             });
18397         }else{
18398             // if the load is cancelled, make sure we notify
18399             // the node that we are done
18400             if(typeof callback == "function"){
18401                 callback();
18402             }
18403         }
18404     },
18405
18406     isLoading : function(){
18407         return this.transId ? true : false;
18408     },
18409
18410     abort : function(){
18411         if(this.isLoading()){
18412             Roo.Ajax.abort(this.transId);
18413         }
18414     },
18415
18416     // private
18417     createNode : function(attr){
18418         // apply baseAttrs, nice idea Corey!
18419         if(this.baseAttrs){
18420             Roo.applyIf(attr, this.baseAttrs);
18421         }
18422         if(this.applyLoader !== false){
18423             attr.loader = this;
18424         }
18425         // uiProvider = depreciated..
18426         
18427         if(typeof(attr.uiProvider) == 'string'){
18428            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18429                 /**  eval:var:attr */ eval(attr.uiProvider);
18430         }
18431         if(typeof(this.uiProviders['default']) != 'undefined') {
18432             attr.uiProvider = this.uiProviders['default'];
18433         }
18434         
18435         this.fireEvent('create', this, attr);
18436         
18437         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18438         return(attr.leaf ?
18439                         new Roo.tree.TreeNode(attr) :
18440                         new Roo.tree.AsyncTreeNode(attr));
18441     },
18442
18443     processResponse : function(response, node, callback){
18444         var json = response.responseText;
18445         try {
18446             
18447             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18448             if (this.root !== false) {
18449                 o = o[this.root];
18450             }
18451             
18452             for(var i = 0, len = o.length; i < len; i++){
18453                 var n = this.createNode(o[i]);
18454                 if(n){
18455                     node.appendChild(n);
18456                 }
18457             }
18458             if(typeof callback == "function"){
18459                 callback(this, node);
18460             }
18461         }catch(e){
18462             this.handleFailure(response);
18463         }
18464     },
18465
18466     handleResponse : function(response){
18467         this.transId = false;
18468         var a = response.argument;
18469         this.processResponse(response, a.node, a.callback);
18470         this.fireEvent("load", this, a.node, response);
18471     },
18472
18473     handleFailure : function(response){
18474         this.transId = false;
18475         var a = response.argument;
18476         this.fireEvent("loadexception", this, a.node, response);
18477         if(typeof a.callback == "function"){
18478             a.callback(this, a.node);
18479         }
18480     }
18481 });/*
18482  * Based on:
18483  * Ext JS Library 1.1.1
18484  * Copyright(c) 2006-2007, Ext JS, LLC.
18485  *
18486  * Originally Released Under LGPL - original licence link has changed is not relivant.
18487  *
18488  * Fork - LGPL
18489  * <script type="text/javascript">
18490  */
18491
18492 /**
18493 * @class Roo.tree.TreeFilter
18494 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18495 * @param {TreePanel} tree
18496 * @param {Object} config (optional)
18497  */
18498 Roo.tree.TreeFilter = function(tree, config){
18499     this.tree = tree;
18500     this.filtered = {};
18501     Roo.apply(this, config);
18502 };
18503
18504 Roo.tree.TreeFilter.prototype = {
18505     clearBlank:false,
18506     reverse:false,
18507     autoClear:false,
18508     remove:false,
18509
18510      /**
18511      * Filter the data by a specific attribute.
18512      * @param {String/RegExp} value Either string that the attribute value
18513      * should start with or a RegExp to test against the attribute
18514      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18515      * @param {TreeNode} startNode (optional) The node to start the filter at.
18516      */
18517     filter : function(value, attr, startNode){
18518         attr = attr || "text";
18519         var f;
18520         if(typeof value == "string"){
18521             var vlen = value.length;
18522             // auto clear empty filter
18523             if(vlen == 0 && this.clearBlank){
18524                 this.clear();
18525                 return;
18526             }
18527             value = value.toLowerCase();
18528             f = function(n){
18529                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18530             };
18531         }else if(value.exec){ // regex?
18532             f = function(n){
18533                 return value.test(n.attributes[attr]);
18534             };
18535         }else{
18536             throw 'Illegal filter type, must be string or regex';
18537         }
18538         this.filterBy(f, null, startNode);
18539         },
18540
18541     /**
18542      * Filter by a function. The passed function will be called with each
18543      * node in the tree (or from the startNode). If the function returns true, the node is kept
18544      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18545      * @param {Function} fn The filter function
18546      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18547      */
18548     filterBy : function(fn, scope, startNode){
18549         startNode = startNode || this.tree.root;
18550         if(this.autoClear){
18551             this.clear();
18552         }
18553         var af = this.filtered, rv = this.reverse;
18554         var f = function(n){
18555             if(n == startNode){
18556                 return true;
18557             }
18558             if(af[n.id]){
18559                 return false;
18560             }
18561             var m = fn.call(scope || n, n);
18562             if(!m || rv){
18563                 af[n.id] = n;
18564                 n.ui.hide();
18565                 return false;
18566             }
18567             return true;
18568         };
18569         startNode.cascade(f);
18570         if(this.remove){
18571            for(var id in af){
18572                if(typeof id != "function"){
18573                    var n = af[id];
18574                    if(n && n.parentNode){
18575                        n.parentNode.removeChild(n);
18576                    }
18577                }
18578            }
18579         }
18580     },
18581
18582     /**
18583      * Clears the current filter. Note: with the "remove" option
18584      * set a filter cannot be cleared.
18585      */
18586     clear : function(){
18587         var t = this.tree;
18588         var af = this.filtered;
18589         for(var id in af){
18590             if(typeof id != "function"){
18591                 var n = af[id];
18592                 if(n){
18593                     n.ui.show();
18594                 }
18595             }
18596         }
18597         this.filtered = {};
18598     }
18599 };
18600 /*
18601  * Based on:
18602  * Ext JS Library 1.1.1
18603  * Copyright(c) 2006-2007, Ext JS, LLC.
18604  *
18605  * Originally Released Under LGPL - original licence link has changed is not relivant.
18606  *
18607  * Fork - LGPL
18608  * <script type="text/javascript">
18609  */
18610  
18611
18612 /**
18613  * @class Roo.tree.TreeSorter
18614  * Provides sorting of nodes in a TreePanel
18615  * 
18616  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18617  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18618  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18619  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18620  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18621  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18622  * @constructor
18623  * @param {TreePanel} tree
18624  * @param {Object} config
18625  */
18626 Roo.tree.TreeSorter = function(tree, config){
18627     Roo.apply(this, config);
18628     tree.on("beforechildrenrendered", this.doSort, this);
18629     tree.on("append", this.updateSort, this);
18630     tree.on("insert", this.updateSort, this);
18631     
18632     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18633     var p = this.property || "text";
18634     var sortType = this.sortType;
18635     var fs = this.folderSort;
18636     var cs = this.caseSensitive === true;
18637     var leafAttr = this.leafAttr || 'leaf';
18638
18639     this.sortFn = function(n1, n2){
18640         if(fs){
18641             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18642                 return 1;
18643             }
18644             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18645                 return -1;
18646             }
18647         }
18648         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18649         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18650         if(v1 < v2){
18651                         return dsc ? +1 : -1;
18652                 }else if(v1 > v2){
18653                         return dsc ? -1 : +1;
18654         }else{
18655                 return 0;
18656         }
18657     };
18658 };
18659
18660 Roo.tree.TreeSorter.prototype = {
18661     doSort : function(node){
18662         node.sort(this.sortFn);
18663     },
18664     
18665     compareNodes : function(n1, n2){
18666         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18667     },
18668     
18669     updateSort : function(tree, node){
18670         if(node.childrenRendered){
18671             this.doSort.defer(1, this, [node]);
18672         }
18673     }
18674 };/*
18675  * Based on:
18676  * Ext JS Library 1.1.1
18677  * Copyright(c) 2006-2007, Ext JS, LLC.
18678  *
18679  * Originally Released Under LGPL - original licence link has changed is not relivant.
18680  *
18681  * Fork - LGPL
18682  * <script type="text/javascript">
18683  */
18684
18685 if(Roo.dd.DropZone){
18686     
18687 Roo.tree.TreeDropZone = function(tree, config){
18688     this.allowParentInsert = false;
18689     this.allowContainerDrop = false;
18690     this.appendOnly = false;
18691     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18692     this.tree = tree;
18693     this.lastInsertClass = "x-tree-no-status";
18694     this.dragOverData = {};
18695 };
18696
18697 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18698     ddGroup : "TreeDD",
18699     
18700     expandDelay : 1000,
18701     
18702     expandNode : function(node){
18703         if(node.hasChildNodes() && !node.isExpanded()){
18704             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18705         }
18706     },
18707     
18708     queueExpand : function(node){
18709         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18710     },
18711     
18712     cancelExpand : function(){
18713         if(this.expandProcId){
18714             clearTimeout(this.expandProcId);
18715             this.expandProcId = false;
18716         }
18717     },
18718     
18719     isValidDropPoint : function(n, pt, dd, e, data){
18720         if(!n || !data){ return false; }
18721         var targetNode = n.node;
18722         var dropNode = data.node;
18723         // default drop rules
18724         if(!(targetNode && targetNode.isTarget && pt)){
18725             return false;
18726         }
18727         if(pt == "append" && targetNode.allowChildren === false){
18728             return false;
18729         }
18730         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18731             return false;
18732         }
18733         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18734             return false;
18735         }
18736         // reuse the object
18737         var overEvent = this.dragOverData;
18738         overEvent.tree = this.tree;
18739         overEvent.target = targetNode;
18740         overEvent.data = data;
18741         overEvent.point = pt;
18742         overEvent.source = dd;
18743         overEvent.rawEvent = e;
18744         overEvent.dropNode = dropNode;
18745         overEvent.cancel = false;  
18746         var result = this.tree.fireEvent("nodedragover", overEvent);
18747         return overEvent.cancel === false && result !== false;
18748     },
18749     
18750     getDropPoint : function(e, n, dd){
18751         var tn = n.node;
18752         if(tn.isRoot){
18753             return tn.allowChildren !== false ? "append" : false; // always append for root
18754         }
18755         var dragEl = n.ddel;
18756         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18757         var y = Roo.lib.Event.getPageY(e);
18758         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18759         
18760         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18761         var noAppend = tn.allowChildren === false;
18762         if(this.appendOnly || tn.parentNode.allowChildren === false){
18763             return noAppend ? false : "append";
18764         }
18765         var noBelow = false;
18766         if(!this.allowParentInsert){
18767             noBelow = tn.hasChildNodes() && tn.isExpanded();
18768         }
18769         var q = (b - t) / (noAppend ? 2 : 3);
18770         if(y >= t && y < (t + q)){
18771             return "above";
18772         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18773             return "below";
18774         }else{
18775             return "append";
18776         }
18777     },
18778     
18779     onNodeEnter : function(n, dd, e, data){
18780         this.cancelExpand();
18781     },
18782     
18783     onNodeOver : function(n, dd, e, data){
18784         var pt = this.getDropPoint(e, n, dd);
18785         var node = n.node;
18786         
18787         // auto node expand check
18788         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18789             this.queueExpand(node);
18790         }else if(pt != "append"){
18791             this.cancelExpand();
18792         }
18793         
18794         // set the insert point style on the target node
18795         var returnCls = this.dropNotAllowed;
18796         if(this.isValidDropPoint(n, pt, dd, e, data)){
18797            if(pt){
18798                var el = n.ddel;
18799                var cls;
18800                if(pt == "above"){
18801                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18802                    cls = "x-tree-drag-insert-above";
18803                }else if(pt == "below"){
18804                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18805                    cls = "x-tree-drag-insert-below";
18806                }else{
18807                    returnCls = "x-tree-drop-ok-append";
18808                    cls = "x-tree-drag-append";
18809                }
18810                if(this.lastInsertClass != cls){
18811                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18812                    this.lastInsertClass = cls;
18813                }
18814            }
18815        }
18816        return returnCls;
18817     },
18818     
18819     onNodeOut : function(n, dd, e, data){
18820         this.cancelExpand();
18821         this.removeDropIndicators(n);
18822     },
18823     
18824     onNodeDrop : function(n, dd, e, data){
18825         var point = this.getDropPoint(e, n, dd);
18826         var targetNode = n.node;
18827         targetNode.ui.startDrop();
18828         if(!this.isValidDropPoint(n, point, dd, e, data)){
18829             targetNode.ui.endDrop();
18830             return false;
18831         }
18832         // first try to find the drop node
18833         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18834         var dropEvent = {
18835             tree : this.tree,
18836             target: targetNode,
18837             data: data,
18838             point: point,
18839             source: dd,
18840             rawEvent: e,
18841             dropNode: dropNode,
18842             cancel: !dropNode   
18843         };
18844         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18845         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18846             targetNode.ui.endDrop();
18847             return false;
18848         }
18849         // allow target changing
18850         targetNode = dropEvent.target;
18851         if(point == "append" && !targetNode.isExpanded()){
18852             targetNode.expand(false, null, function(){
18853                 this.completeDrop(dropEvent);
18854             }.createDelegate(this));
18855         }else{
18856             this.completeDrop(dropEvent);
18857         }
18858         return true;
18859     },
18860     
18861     completeDrop : function(de){
18862         var ns = de.dropNode, p = de.point, t = de.target;
18863         if(!(ns instanceof Array)){
18864             ns = [ns];
18865         }
18866         var n;
18867         for(var i = 0, len = ns.length; i < len; i++){
18868             n = ns[i];
18869             if(p == "above"){
18870                 t.parentNode.insertBefore(n, t);
18871             }else if(p == "below"){
18872                 t.parentNode.insertBefore(n, t.nextSibling);
18873             }else{
18874                 t.appendChild(n);
18875             }
18876         }
18877         n.ui.focus();
18878         if(this.tree.hlDrop){
18879             n.ui.highlight();
18880         }
18881         t.ui.endDrop();
18882         this.tree.fireEvent("nodedrop", de);
18883     },
18884     
18885     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18886         if(this.tree.hlDrop){
18887             dropNode.ui.focus();
18888             dropNode.ui.highlight();
18889         }
18890         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18891     },
18892     
18893     getTree : function(){
18894         return this.tree;
18895     },
18896     
18897     removeDropIndicators : function(n){
18898         if(n && n.ddel){
18899             var el = n.ddel;
18900             Roo.fly(el).removeClass([
18901                     "x-tree-drag-insert-above",
18902                     "x-tree-drag-insert-below",
18903                     "x-tree-drag-append"]);
18904             this.lastInsertClass = "_noclass";
18905         }
18906     },
18907     
18908     beforeDragDrop : function(target, e, id){
18909         this.cancelExpand();
18910         return true;
18911     },
18912     
18913     afterRepair : function(data){
18914         if(data && Roo.enableFx){
18915             data.node.ui.highlight();
18916         }
18917         this.hideProxy();
18918     }    
18919 });
18920
18921 }
18922 /*
18923  * Based on:
18924  * Ext JS Library 1.1.1
18925  * Copyright(c) 2006-2007, Ext JS, LLC.
18926  *
18927  * Originally Released Under LGPL - original licence link has changed is not relivant.
18928  *
18929  * Fork - LGPL
18930  * <script type="text/javascript">
18931  */
18932  
18933
18934 if(Roo.dd.DragZone){
18935 Roo.tree.TreeDragZone = function(tree, config){
18936     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18937     this.tree = tree;
18938 };
18939
18940 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18941     ddGroup : "TreeDD",
18942     
18943     onBeforeDrag : function(data, e){
18944         var n = data.node;
18945         return n && n.draggable && !n.disabled;
18946     },
18947     
18948     onInitDrag : function(e){
18949         var data = this.dragData;
18950         this.tree.getSelectionModel().select(data.node);
18951         this.proxy.update("");
18952         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18953         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18954     },
18955     
18956     getRepairXY : function(e, data){
18957         return data.node.ui.getDDRepairXY();
18958     },
18959     
18960     onEndDrag : function(data, e){
18961         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18962     },
18963     
18964     onValidDrop : function(dd, e, id){
18965         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18966         this.hideProxy();
18967     },
18968     
18969     beforeInvalidDrop : function(e, id){
18970         // this scrolls the original position back into view
18971         var sm = this.tree.getSelectionModel();
18972         sm.clearSelections();
18973         sm.select(this.dragData.node);
18974     }
18975 });
18976 }/*
18977  * Based on:
18978  * Ext JS Library 1.1.1
18979  * Copyright(c) 2006-2007, Ext JS, LLC.
18980  *
18981  * Originally Released Under LGPL - original licence link has changed is not relivant.
18982  *
18983  * Fork - LGPL
18984  * <script type="text/javascript">
18985  */
18986 /**
18987  * @class Roo.tree.TreeEditor
18988  * @extends Roo.Editor
18989  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18990  * as the editor field.
18991  * @constructor
18992  * @param {TreePanel} tree
18993  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18994  */
18995 Roo.tree.TreeEditor = function(tree, config){
18996     config = config || {};
18997     var field = config.events ? config : new Roo.form.TextField(config);
18998     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18999
19000     this.tree = tree;
19001
19002     tree.on('beforeclick', this.beforeNodeClick, this);
19003     tree.getTreeEl().on('mousedown', this.hide, this);
19004     this.on('complete', this.updateNode, this);
19005     this.on('beforestartedit', this.fitToTree, this);
19006     this.on('startedit', this.bindScroll, this, {delay:10});
19007     this.on('specialkey', this.onSpecialKey, this);
19008 };
19009
19010 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19011     /**
19012      * @cfg {String} alignment
19013      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19014      */
19015     alignment: "l-l",
19016     // inherit
19017     autoSize: false,
19018     /**
19019      * @cfg {Boolean} hideEl
19020      * True to hide the bound element while the editor is displayed (defaults to false)
19021      */
19022     hideEl : false,
19023     /**
19024      * @cfg {String} cls
19025      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19026      */
19027     cls: "x-small-editor x-tree-editor",
19028     /**
19029      * @cfg {Boolean} shim
19030      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19031      */
19032     shim:false,
19033     // inherit
19034     shadow:"frame",
19035     /**
19036      * @cfg {Number} maxWidth
19037      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19038      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19039      * scroll and client offsets into account prior to each edit.
19040      */
19041     maxWidth: 250,
19042
19043     editDelay : 350,
19044
19045     // private
19046     fitToTree : function(ed, el){
19047         var td = this.tree.getTreeEl().dom, nd = el.dom;
19048         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19049             td.scrollLeft = nd.offsetLeft;
19050         }
19051         var w = Math.min(
19052                 this.maxWidth,
19053                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19054         this.setSize(w, '');
19055     },
19056
19057     // private
19058     triggerEdit : function(node){
19059         this.completeEdit();
19060         this.editNode = node;
19061         this.startEdit(node.ui.textNode, node.text);
19062     },
19063
19064     // private
19065     bindScroll : function(){
19066         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19067     },
19068
19069     // private
19070     beforeNodeClick : function(node, e){
19071         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19072         this.lastClick = new Date();
19073         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19074             e.stopEvent();
19075             this.triggerEdit(node);
19076             return false;
19077         }
19078     },
19079
19080     // private
19081     updateNode : function(ed, value){
19082         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19083         this.editNode.setText(value);
19084     },
19085
19086     // private
19087     onHide : function(){
19088         Roo.tree.TreeEditor.superclass.onHide.call(this);
19089         if(this.editNode){
19090             this.editNode.ui.focus();
19091         }
19092     },
19093
19094     // private
19095     onSpecialKey : function(field, e){
19096         var k = e.getKey();
19097         if(k == e.ESC){
19098             e.stopEvent();
19099             this.cancelEdit();
19100         }else if(k == e.ENTER && !e.hasModifier()){
19101             e.stopEvent();
19102             this.completeEdit();
19103         }
19104     }
19105 });//<Script type="text/javascript">
19106 /*
19107  * Based on:
19108  * Ext JS Library 1.1.1
19109  * Copyright(c) 2006-2007, Ext JS, LLC.
19110  *
19111  * Originally Released Under LGPL - original licence link has changed is not relivant.
19112  *
19113  * Fork - LGPL
19114  * <script type="text/javascript">
19115  */
19116  
19117 /**
19118  * Not documented??? - probably should be...
19119  */
19120
19121 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19122     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19123     
19124     renderElements : function(n, a, targetNode, bulkRender){
19125         //consel.log("renderElements?");
19126         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19127
19128         var t = n.getOwnerTree();
19129         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19130         
19131         var cols = t.columns;
19132         var bw = t.borderWidth;
19133         var c = cols[0];
19134         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19135          var cb = typeof a.checked == "boolean";
19136         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19137         var colcls = 'x-t-' + tid + '-c0';
19138         var buf = [
19139             '<li class="x-tree-node">',
19140             
19141                 
19142                 '<div class="x-tree-node-el ', a.cls,'">',
19143                     // extran...
19144                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19145                 
19146                 
19147                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19148                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19149                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19150                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19151                            (a.iconCls ? ' '+a.iconCls : ''),
19152                            '" unselectable="on" />',
19153                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19154                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19155                              
19156                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19157                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19158                             '<span unselectable="on" qtip="' + tx + '">',
19159                              tx,
19160                              '</span></a>' ,
19161                     '</div>',
19162                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19163                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19164                  ];
19165         for(var i = 1, len = cols.length; i < len; i++){
19166             c = cols[i];
19167             colcls = 'x-t-' + tid + '-c' +i;
19168             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19169             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19170                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19171                       "</div>");
19172          }
19173          
19174          buf.push(
19175             '</a>',
19176             '<div class="x-clear"></div></div>',
19177             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19178             "</li>");
19179         
19180         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19181             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19182                                 n.nextSibling.ui.getEl(), buf.join(""));
19183         }else{
19184             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19185         }
19186         var el = this.wrap.firstChild;
19187         this.elRow = el;
19188         this.elNode = el.firstChild;
19189         this.ranchor = el.childNodes[1];
19190         this.ctNode = this.wrap.childNodes[1];
19191         var cs = el.firstChild.childNodes;
19192         this.indentNode = cs[0];
19193         this.ecNode = cs[1];
19194         this.iconNode = cs[2];
19195         var index = 3;
19196         if(cb){
19197             this.checkbox = cs[3];
19198             index++;
19199         }
19200         this.anchor = cs[index];
19201         
19202         this.textNode = cs[index].firstChild;
19203         
19204         //el.on("click", this.onClick, this);
19205         //el.on("dblclick", this.onDblClick, this);
19206         
19207         
19208        // console.log(this);
19209     },
19210     initEvents : function(){
19211         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19212         
19213             
19214         var a = this.ranchor;
19215
19216         var el = Roo.get(a);
19217
19218         if(Roo.isOpera){ // opera render bug ignores the CSS
19219             el.setStyle("text-decoration", "none");
19220         }
19221
19222         el.on("click", this.onClick, this);
19223         el.on("dblclick", this.onDblClick, this);
19224         el.on("contextmenu", this.onContextMenu, this);
19225         
19226     },
19227     
19228     /*onSelectedChange : function(state){
19229         if(state){
19230             this.focus();
19231             this.addClass("x-tree-selected");
19232         }else{
19233             //this.blur();
19234             this.removeClass("x-tree-selected");
19235         }
19236     },*/
19237     addClass : function(cls){
19238         if(this.elRow){
19239             Roo.fly(this.elRow).addClass(cls);
19240         }
19241         
19242     },
19243     
19244     
19245     removeClass : function(cls){
19246         if(this.elRow){
19247             Roo.fly(this.elRow).removeClass(cls);
19248         }
19249     }
19250
19251     
19252     
19253 });//<Script type="text/javascript">
19254
19255 /*
19256  * Based on:
19257  * Ext JS Library 1.1.1
19258  * Copyright(c) 2006-2007, Ext JS, LLC.
19259  *
19260  * Originally Released Under LGPL - original licence link has changed is not relivant.
19261  *
19262  * Fork - LGPL
19263  * <script type="text/javascript">
19264  */
19265  
19266
19267 /**
19268  * @class Roo.tree.ColumnTree
19269  * @extends Roo.data.TreePanel
19270  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19271  * @cfg {int} borderWidth  compined right/left border allowance
19272  * @constructor
19273  * @param {String/HTMLElement/Element} el The container element
19274  * @param {Object} config
19275  */
19276 Roo.tree.ColumnTree =  function(el, config)
19277 {
19278    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19279    this.addEvents({
19280         /**
19281         * @event resize
19282         * Fire this event on a container when it resizes
19283         * @param {int} w Width
19284         * @param {int} h Height
19285         */
19286        "resize" : true
19287     });
19288     this.on('resize', this.onResize, this);
19289 };
19290
19291 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19292     //lines:false,
19293     
19294     
19295     borderWidth: Roo.isBorderBox ? 0 : 2, 
19296     headEls : false,
19297     
19298     render : function(){
19299         // add the header.....
19300        
19301         Roo.tree.ColumnTree.superclass.render.apply(this);
19302         
19303         this.el.addClass('x-column-tree');
19304         
19305         this.headers = this.el.createChild(
19306             {cls:'x-tree-headers'},this.innerCt.dom);
19307    
19308         var cols = this.columns, c;
19309         var totalWidth = 0;
19310         this.headEls = [];
19311         var  len = cols.length;
19312         for(var i = 0; i < len; i++){
19313              c = cols[i];
19314              totalWidth += c.width;
19315             this.headEls.push(this.headers.createChild({
19316                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19317                  cn: {
19318                      cls:'x-tree-hd-text',
19319                      html: c.header
19320                  },
19321                  style:'width:'+(c.width-this.borderWidth)+'px;'
19322              }));
19323         }
19324         this.headers.createChild({cls:'x-clear'});
19325         // prevent floats from wrapping when clipped
19326         this.headers.setWidth(totalWidth);
19327         //this.innerCt.setWidth(totalWidth);
19328         this.innerCt.setStyle({ overflow: 'auto' });
19329         this.onResize(this.width, this.height);
19330              
19331         
19332     },
19333     onResize : function(w,h)
19334     {
19335         this.height = h;
19336         this.width = w;
19337         // resize cols..
19338         this.innerCt.setWidth(this.width);
19339         this.innerCt.setHeight(this.height-20);
19340         
19341         // headers...
19342         var cols = this.columns, c;
19343         var totalWidth = 0;
19344         var expEl = false;
19345         var len = cols.length;
19346         for(var i = 0; i < len; i++){
19347             c = cols[i];
19348             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19349                 // it's the expander..
19350                 expEl  = this.headEls[i];
19351                 continue;
19352             }
19353             totalWidth += c.width;
19354             
19355         }
19356         if (expEl) {
19357             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19358         }
19359         this.headers.setWidth(w-20);
19360
19361         
19362         
19363         
19364     }
19365 });
19366 /*
19367  * Based on:
19368  * Ext JS Library 1.1.1
19369  * Copyright(c) 2006-2007, Ext JS, LLC.
19370  *
19371  * Originally Released Under LGPL - original licence link has changed is not relivant.
19372  *
19373  * Fork - LGPL
19374  * <script type="text/javascript">
19375  */
19376  
19377 /**
19378  * @class Roo.menu.Menu
19379  * @extends Roo.util.Observable
19380  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19381  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19382  * @constructor
19383  * Creates a new Menu
19384  * @param {Object} config Configuration options
19385  */
19386 Roo.menu.Menu = function(config){
19387     Roo.apply(this, config);
19388     this.id = this.id || Roo.id();
19389     this.addEvents({
19390         /**
19391          * @event beforeshow
19392          * Fires before this menu is displayed
19393          * @param {Roo.menu.Menu} this
19394          */
19395         beforeshow : true,
19396         /**
19397          * @event beforehide
19398          * Fires before this menu is hidden
19399          * @param {Roo.menu.Menu} this
19400          */
19401         beforehide : true,
19402         /**
19403          * @event show
19404          * Fires after this menu is displayed
19405          * @param {Roo.menu.Menu} this
19406          */
19407         show : true,
19408         /**
19409          * @event hide
19410          * Fires after this menu is hidden
19411          * @param {Roo.menu.Menu} this
19412          */
19413         hide : true,
19414         /**
19415          * @event click
19416          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19417          * @param {Roo.menu.Menu} this
19418          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19419          * @param {Roo.EventObject} e
19420          */
19421         click : true,
19422         /**
19423          * @event mouseover
19424          * Fires when the mouse is hovering over this menu
19425          * @param {Roo.menu.Menu} this
19426          * @param {Roo.EventObject} e
19427          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19428          */
19429         mouseover : true,
19430         /**
19431          * @event mouseout
19432          * Fires when the mouse exits 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         mouseout : true,
19438         /**
19439          * @event itemclick
19440          * Fires when a menu item contained in this menu is clicked
19441          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19442          * @param {Roo.EventObject} e
19443          */
19444         itemclick: true
19445     });
19446     if (this.registerMenu) {
19447         Roo.menu.MenuMgr.register(this);
19448     }
19449     
19450     var mis = this.items;
19451     this.items = new Roo.util.MixedCollection();
19452     if(mis){
19453         this.add.apply(this, mis);
19454     }
19455 };
19456
19457 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19458     /**
19459      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19460      */
19461     minWidth : 120,
19462     /**
19463      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19464      * for bottom-right shadow (defaults to "sides")
19465      */
19466     shadow : "sides",
19467     /**
19468      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19469      * this menu (defaults to "tl-tr?")
19470      */
19471     subMenuAlign : "tl-tr?",
19472     /**
19473      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19474      * relative to its element of origin (defaults to "tl-bl?")
19475      */
19476     defaultAlign : "tl-bl?",
19477     /**
19478      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19479      */
19480     allowOtherMenus : false,
19481     /**
19482      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19483      */
19484     registerMenu : true,
19485
19486     hidden:true,
19487
19488     // private
19489     render : function(){
19490         if(this.el){
19491             return;
19492         }
19493         var el = this.el = new Roo.Layer({
19494             cls: "x-menu",
19495             shadow:this.shadow,
19496             constrain: false,
19497             parentEl: this.parentEl || document.body,
19498             zindex:15000
19499         });
19500
19501         this.keyNav = new Roo.menu.MenuNav(this);
19502
19503         if(this.plain){
19504             el.addClass("x-menu-plain");
19505         }
19506         if(this.cls){
19507             el.addClass(this.cls);
19508         }
19509         // generic focus element
19510         this.focusEl = el.createChild({
19511             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19512         });
19513         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19514         ul.on("click", this.onClick, this);
19515         ul.on("mouseover", this.onMouseOver, this);
19516         ul.on("mouseout", this.onMouseOut, this);
19517         this.items.each(function(item){
19518             var li = document.createElement("li");
19519             li.className = "x-menu-list-item";
19520             ul.dom.appendChild(li);
19521             item.render(li, this);
19522         }, this);
19523         this.ul = ul;
19524         this.autoWidth();
19525     },
19526
19527     // private
19528     autoWidth : function(){
19529         var el = this.el, ul = this.ul;
19530         if(!el){
19531             return;
19532         }
19533         var w = this.width;
19534         if(w){
19535             el.setWidth(w);
19536         }else if(Roo.isIE){
19537             el.setWidth(this.minWidth);
19538             var t = el.dom.offsetWidth; // force recalc
19539             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19540         }
19541     },
19542
19543     // private
19544     delayAutoWidth : function(){
19545         if(this.rendered){
19546             if(!this.awTask){
19547                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19548             }
19549             this.awTask.delay(20);
19550         }
19551     },
19552
19553     // private
19554     findTargetItem : function(e){
19555         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19556         if(t && t.menuItemId){
19557             return this.items.get(t.menuItemId);
19558         }
19559     },
19560
19561     // private
19562     onClick : function(e){
19563         var t;
19564         if(t = this.findTargetItem(e)){
19565             t.onClick(e);
19566             this.fireEvent("click", this, t, e);
19567         }
19568     },
19569
19570     // private
19571     setActiveItem : function(item, autoExpand){
19572         if(item != this.activeItem){
19573             if(this.activeItem){
19574                 this.activeItem.deactivate();
19575             }
19576             this.activeItem = item;
19577             item.activate(autoExpand);
19578         }else if(autoExpand){
19579             item.expandMenu();
19580         }
19581     },
19582
19583     // private
19584     tryActivate : function(start, step){
19585         var items = this.items;
19586         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19587             var item = items.get(i);
19588             if(!item.disabled && item.canActivate){
19589                 this.setActiveItem(item, false);
19590                 return item;
19591             }
19592         }
19593         return false;
19594     },
19595
19596     // private
19597     onMouseOver : function(e){
19598         var t;
19599         if(t = this.findTargetItem(e)){
19600             if(t.canActivate && !t.disabled){
19601                 this.setActiveItem(t, true);
19602             }
19603         }
19604         this.fireEvent("mouseover", this, e, t);
19605     },
19606
19607     // private
19608     onMouseOut : function(e){
19609         var t;
19610         if(t = this.findTargetItem(e)){
19611             if(t == this.activeItem && t.shouldDeactivate(e)){
19612                 this.activeItem.deactivate();
19613                 delete this.activeItem;
19614             }
19615         }
19616         this.fireEvent("mouseout", this, e, t);
19617     },
19618
19619     /**
19620      * Read-only.  Returns true if the menu is currently displayed, else false.
19621      * @type Boolean
19622      */
19623     isVisible : function(){
19624         return this.el && !this.hidden;
19625     },
19626
19627     /**
19628      * Displays this menu relative to another element
19629      * @param {String/HTMLElement/Roo.Element} element The element to align to
19630      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19631      * the element (defaults to this.defaultAlign)
19632      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19633      */
19634     show : function(el, pos, parentMenu){
19635         this.parentMenu = parentMenu;
19636         if(!this.el){
19637             this.render();
19638         }
19639         this.fireEvent("beforeshow", this);
19640         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19641     },
19642
19643     /**
19644      * Displays this menu at a specific xy position
19645      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19646      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19647      */
19648     showAt : function(xy, parentMenu, /* private: */_e){
19649         this.parentMenu = parentMenu;
19650         if(!this.el){
19651             this.render();
19652         }
19653         if(_e !== false){
19654             this.fireEvent("beforeshow", this);
19655             xy = this.el.adjustForConstraints(xy);
19656         }
19657         this.el.setXY(xy);
19658         this.el.show();
19659         this.hidden = false;
19660         this.focus();
19661         this.fireEvent("show", this);
19662     },
19663
19664     focus : function(){
19665         if(!this.hidden){
19666             this.doFocus.defer(50, this);
19667         }
19668     },
19669
19670     doFocus : function(){
19671         if(!this.hidden){
19672             this.focusEl.focus();
19673         }
19674     },
19675
19676     /**
19677      * Hides this menu and optionally all parent menus
19678      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19679      */
19680     hide : function(deep){
19681         if(this.el && this.isVisible()){
19682             this.fireEvent("beforehide", this);
19683             if(this.activeItem){
19684                 this.activeItem.deactivate();
19685                 this.activeItem = null;
19686             }
19687             this.el.hide();
19688             this.hidden = true;
19689             this.fireEvent("hide", this);
19690         }
19691         if(deep === true && this.parentMenu){
19692             this.parentMenu.hide(true);
19693         }
19694     },
19695
19696     /**
19697      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19698      * Any of the following are valid:
19699      * <ul>
19700      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19701      * <li>An HTMLElement object which will be converted to a menu item</li>
19702      * <li>A menu item config object that will be created as a new menu item</li>
19703      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19704      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19705      * </ul>
19706      * Usage:
19707      * <pre><code>
19708 // Create the menu
19709 var menu = new Roo.menu.Menu();
19710
19711 // Create a menu item to add by reference
19712 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19713
19714 // Add a bunch of items at once using different methods.
19715 // Only the last item added will be returned.
19716 var item = menu.add(
19717     menuItem,                // add existing item by ref
19718     'Dynamic Item',          // new TextItem
19719     '-',                     // new separator
19720     { text: 'Config Item' }  // new item by config
19721 );
19722 </code></pre>
19723      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19724      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19725      */
19726     add : function(){
19727         var a = arguments, l = a.length, item;
19728         for(var i = 0; i < l; i++){
19729             var el = a[i];
19730             if ((typeof(el) == "object") && el.xtype && el.xns) {
19731                 el = Roo.factory(el, Roo.menu);
19732             }
19733             
19734             if(el.render){ // some kind of Item
19735                 item = this.addItem(el);
19736             }else if(typeof el == "string"){ // string
19737                 if(el == "separator" || el == "-"){
19738                     item = this.addSeparator();
19739                 }else{
19740                     item = this.addText(el);
19741                 }
19742             }else if(el.tagName || el.el){ // element
19743                 item = this.addElement(el);
19744             }else if(typeof el == "object"){ // must be menu item config?
19745                 item = this.addMenuItem(el);
19746             }
19747         }
19748         return item;
19749     },
19750
19751     /**
19752      * Returns this menu's underlying {@link Roo.Element} object
19753      * @return {Roo.Element} The element
19754      */
19755     getEl : function(){
19756         if(!this.el){
19757             this.render();
19758         }
19759         return this.el;
19760     },
19761
19762     /**
19763      * Adds a separator bar to the menu
19764      * @return {Roo.menu.Item} The menu item that was added
19765      */
19766     addSeparator : function(){
19767         return this.addItem(new Roo.menu.Separator());
19768     },
19769
19770     /**
19771      * Adds an {@link Roo.Element} object to the menu
19772      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19773      * @return {Roo.menu.Item} The menu item that was added
19774      */
19775     addElement : function(el){
19776         return this.addItem(new Roo.menu.BaseItem(el));
19777     },
19778
19779     /**
19780      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19781      * @param {Roo.menu.Item} item The menu item to add
19782      * @return {Roo.menu.Item} The menu item that was added
19783      */
19784     addItem : function(item){
19785         this.items.add(item);
19786         if(this.ul){
19787             var li = document.createElement("li");
19788             li.className = "x-menu-list-item";
19789             this.ul.dom.appendChild(li);
19790             item.render(li, this);
19791             this.delayAutoWidth();
19792         }
19793         return item;
19794     },
19795
19796     /**
19797      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19798      * @param {Object} config A MenuItem config object
19799      * @return {Roo.menu.Item} The menu item that was added
19800      */
19801     addMenuItem : function(config){
19802         if(!(config instanceof Roo.menu.Item)){
19803             if(typeof config.checked == "boolean"){ // must be check menu item config?
19804                 config = new Roo.menu.CheckItem(config);
19805             }else{
19806                 config = new Roo.menu.Item(config);
19807             }
19808         }
19809         return this.addItem(config);
19810     },
19811
19812     /**
19813      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19814      * @param {String} text The text to display in the menu item
19815      * @return {Roo.menu.Item} The menu item that was added
19816      */
19817     addText : function(text){
19818         return this.addItem(new Roo.menu.TextItem({ text : text }));
19819     },
19820
19821     /**
19822      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19823      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19824      * @param {Roo.menu.Item} item The menu item to add
19825      * @return {Roo.menu.Item} The menu item that was added
19826      */
19827     insert : function(index, item){
19828         this.items.insert(index, item);
19829         if(this.ul){
19830             var li = document.createElement("li");
19831             li.className = "x-menu-list-item";
19832             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19833             item.render(li, this);
19834             this.delayAutoWidth();
19835         }
19836         return item;
19837     },
19838
19839     /**
19840      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19841      * @param {Roo.menu.Item} item The menu item to remove
19842      */
19843     remove : function(item){
19844         this.items.removeKey(item.id);
19845         item.destroy();
19846     },
19847
19848     /**
19849      * Removes and destroys all items in the menu
19850      */
19851     removeAll : function(){
19852         var f;
19853         while(f = this.items.first()){
19854             this.remove(f);
19855         }
19856     }
19857 });
19858
19859 // MenuNav is a private utility class used internally by the Menu
19860 Roo.menu.MenuNav = function(menu){
19861     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19862     this.scope = this.menu = menu;
19863 };
19864
19865 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19866     doRelay : function(e, h){
19867         var k = e.getKey();
19868         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19869             this.menu.tryActivate(0, 1);
19870             return false;
19871         }
19872         return h.call(this.scope || this, e, this.menu);
19873     },
19874
19875     up : function(e, m){
19876         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19877             m.tryActivate(m.items.length-1, -1);
19878         }
19879     },
19880
19881     down : function(e, m){
19882         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19883             m.tryActivate(0, 1);
19884         }
19885     },
19886
19887     right : function(e, m){
19888         if(m.activeItem){
19889             m.activeItem.expandMenu(true);
19890         }
19891     },
19892
19893     left : function(e, m){
19894         m.hide();
19895         if(m.parentMenu && m.parentMenu.activeItem){
19896             m.parentMenu.activeItem.activate();
19897         }
19898     },
19899
19900     enter : function(e, m){
19901         if(m.activeItem){
19902             e.stopPropagation();
19903             m.activeItem.onClick(e);
19904             m.fireEvent("click", this, m.activeItem);
19905             return true;
19906         }
19907     }
19908 });/*
19909  * Based on:
19910  * Ext JS Library 1.1.1
19911  * Copyright(c) 2006-2007, Ext JS, LLC.
19912  *
19913  * Originally Released Under LGPL - original licence link has changed is not relivant.
19914  *
19915  * Fork - LGPL
19916  * <script type="text/javascript">
19917  */
19918  
19919 /**
19920  * @class Roo.menu.MenuMgr
19921  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19922  * @singleton
19923  */
19924 Roo.menu.MenuMgr = function(){
19925    var menus, active, groups = {}, attached = false, lastShow = new Date();
19926
19927    // private - called when first menu is created
19928    function init(){
19929        menus = {};
19930        active = new Roo.util.MixedCollection();
19931        Roo.get(document).addKeyListener(27, function(){
19932            if(active.length > 0){
19933                hideAll();
19934            }
19935        });
19936    }
19937
19938    // private
19939    function hideAll(){
19940        if(active && active.length > 0){
19941            var c = active.clone();
19942            c.each(function(m){
19943                m.hide();
19944            });
19945        }
19946    }
19947
19948    // private
19949    function onHide(m){
19950        active.remove(m);
19951        if(active.length < 1){
19952            Roo.get(document).un("mousedown", onMouseDown);
19953            attached = false;
19954        }
19955    }
19956
19957    // private
19958    function onShow(m){
19959        var last = active.last();
19960        lastShow = new Date();
19961        active.add(m);
19962        if(!attached){
19963            Roo.get(document).on("mousedown", onMouseDown);
19964            attached = true;
19965        }
19966        if(m.parentMenu){
19967           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19968           m.parentMenu.activeChild = m;
19969        }else if(last && last.isVisible()){
19970           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19971        }
19972    }
19973
19974    // private
19975    function onBeforeHide(m){
19976        if(m.activeChild){
19977            m.activeChild.hide();
19978        }
19979        if(m.autoHideTimer){
19980            clearTimeout(m.autoHideTimer);
19981            delete m.autoHideTimer;
19982        }
19983    }
19984
19985    // private
19986    function onBeforeShow(m){
19987        var pm = m.parentMenu;
19988        if(!pm && !m.allowOtherMenus){
19989            hideAll();
19990        }else if(pm && pm.activeChild && active != m){
19991            pm.activeChild.hide();
19992        }
19993    }
19994
19995    // private
19996    function onMouseDown(e){
19997        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19998            hideAll();
19999        }
20000    }
20001
20002    // private
20003    function onBeforeCheck(mi, state){
20004        if(state){
20005            var g = groups[mi.group];
20006            for(var i = 0, l = g.length; i < l; i++){
20007                if(g[i] != mi){
20008                    g[i].setChecked(false);
20009                }
20010            }
20011        }
20012    }
20013
20014    return {
20015
20016        /**
20017         * Hides all menus that are currently visible
20018         */
20019        hideAll : function(){
20020             hideAll();  
20021        },
20022
20023        // private
20024        register : function(menu){
20025            if(!menus){
20026                init();
20027            }
20028            menus[menu.id] = menu;
20029            menu.on("beforehide", onBeforeHide);
20030            menu.on("hide", onHide);
20031            menu.on("beforeshow", onBeforeShow);
20032            menu.on("show", onShow);
20033            var g = menu.group;
20034            if(g && menu.events["checkchange"]){
20035                if(!groups[g]){
20036                    groups[g] = [];
20037                }
20038                groups[g].push(menu);
20039                menu.on("checkchange", onCheck);
20040            }
20041        },
20042
20043         /**
20044          * Returns a {@link Roo.menu.Menu} object
20045          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20046          * be used to generate and return a new Menu instance.
20047          */
20048        get : function(menu){
20049            if(typeof menu == "string"){ // menu id
20050                return menus[menu];
20051            }else if(menu.events){  // menu instance
20052                return menu;
20053            }else if(typeof menu.length == 'number'){ // array of menu items?
20054                return new Roo.menu.Menu({items:menu});
20055            }else{ // otherwise, must be a config
20056                return new Roo.menu.Menu(menu);
20057            }
20058        },
20059
20060        // private
20061        unregister : function(menu){
20062            delete menus[menu.id];
20063            menu.un("beforehide", onBeforeHide);
20064            menu.un("hide", onHide);
20065            menu.un("beforeshow", onBeforeShow);
20066            menu.un("show", onShow);
20067            var g = menu.group;
20068            if(g && menu.events["checkchange"]){
20069                groups[g].remove(menu);
20070                menu.un("checkchange", onCheck);
20071            }
20072        },
20073
20074        // private
20075        registerCheckable : function(menuItem){
20076            var g = menuItem.group;
20077            if(g){
20078                if(!groups[g]){
20079                    groups[g] = [];
20080                }
20081                groups[g].push(menuItem);
20082                menuItem.on("beforecheckchange", onBeforeCheck);
20083            }
20084        },
20085
20086        // private
20087        unregisterCheckable : function(menuItem){
20088            var g = menuItem.group;
20089            if(g){
20090                groups[g].remove(menuItem);
20091                menuItem.un("beforecheckchange", onBeforeCheck);
20092            }
20093        }
20094    };
20095 }();/*
20096  * Based on:
20097  * Ext JS Library 1.1.1
20098  * Copyright(c) 2006-2007, Ext JS, LLC.
20099  *
20100  * Originally Released Under LGPL - original licence link has changed is not relivant.
20101  *
20102  * Fork - LGPL
20103  * <script type="text/javascript">
20104  */
20105  
20106
20107 /**
20108  * @class Roo.menu.BaseItem
20109  * @extends Roo.Component
20110  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20111  * management and base configuration options shared by all menu components.
20112  * @constructor
20113  * Creates a new BaseItem
20114  * @param {Object} config Configuration options
20115  */
20116 Roo.menu.BaseItem = function(config){
20117     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20118
20119     this.addEvents({
20120         /**
20121          * @event click
20122          * Fires when this item is clicked
20123          * @param {Roo.menu.BaseItem} this
20124          * @param {Roo.EventObject} e
20125          */
20126         click: true,
20127         /**
20128          * @event activate
20129          * Fires when this item is activated
20130          * @param {Roo.menu.BaseItem} this
20131          */
20132         activate : true,
20133         /**
20134          * @event deactivate
20135          * Fires when this item is deactivated
20136          * @param {Roo.menu.BaseItem} this
20137          */
20138         deactivate : true
20139     });
20140
20141     if(this.handler){
20142         this.on("click", this.handler, this.scope, true);
20143     }
20144 };
20145
20146 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20147     /**
20148      * @cfg {Function} handler
20149      * A function that will handle the click event of this menu item (defaults to undefined)
20150      */
20151     /**
20152      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20153      */
20154     canActivate : false,
20155     /**
20156      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20157      */
20158     activeClass : "x-menu-item-active",
20159     /**
20160      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20161      */
20162     hideOnClick : true,
20163     /**
20164      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20165      */
20166     hideDelay : 100,
20167
20168     // private
20169     ctype: "Roo.menu.BaseItem",
20170
20171     // private
20172     actionMode : "container",
20173
20174     // private
20175     render : function(container, parentMenu){
20176         this.parentMenu = parentMenu;
20177         Roo.menu.BaseItem.superclass.render.call(this, container);
20178         this.container.menuItemId = this.id;
20179     },
20180
20181     // private
20182     onRender : function(container, position){
20183         this.el = Roo.get(this.el);
20184         container.dom.appendChild(this.el.dom);
20185     },
20186
20187     // private
20188     onClick : function(e){
20189         if(!this.disabled && this.fireEvent("click", this, e) !== false
20190                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20191             this.handleClick(e);
20192         }else{
20193             e.stopEvent();
20194         }
20195     },
20196
20197     // private
20198     activate : function(){
20199         if(this.disabled){
20200             return false;
20201         }
20202         var li = this.container;
20203         li.addClass(this.activeClass);
20204         this.region = li.getRegion().adjust(2, 2, -2, -2);
20205         this.fireEvent("activate", this);
20206         return true;
20207     },
20208
20209     // private
20210     deactivate : function(){
20211         this.container.removeClass(this.activeClass);
20212         this.fireEvent("deactivate", this);
20213     },
20214
20215     // private
20216     shouldDeactivate : function(e){
20217         return !this.region || !this.region.contains(e.getPoint());
20218     },
20219
20220     // private
20221     handleClick : function(e){
20222         if(this.hideOnClick){
20223             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20224         }
20225     },
20226
20227     // private
20228     expandMenu : function(autoActivate){
20229         // do nothing
20230     },
20231
20232     // private
20233     hideMenu : function(){
20234         // do nothing
20235     }
20236 });/*
20237  * Based on:
20238  * Ext JS Library 1.1.1
20239  * Copyright(c) 2006-2007, Ext JS, LLC.
20240  *
20241  * Originally Released Under LGPL - original licence link has changed is not relivant.
20242  *
20243  * Fork - LGPL
20244  * <script type="text/javascript">
20245  */
20246  
20247 /**
20248  * @class Roo.menu.Adapter
20249  * @extends Roo.menu.BaseItem
20250  * 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.
20251  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20252  * @constructor
20253  * Creates a new Adapter
20254  * @param {Object} config Configuration options
20255  */
20256 Roo.menu.Adapter = function(component, config){
20257     Roo.menu.Adapter.superclass.constructor.call(this, config);
20258     this.component = component;
20259 };
20260 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20261     // private
20262     canActivate : true,
20263
20264     // private
20265     onRender : function(container, position){
20266         this.component.render(container);
20267         this.el = this.component.getEl();
20268     },
20269
20270     // private
20271     activate : function(){
20272         if(this.disabled){
20273             return false;
20274         }
20275         this.component.focus();
20276         this.fireEvent("activate", this);
20277         return true;
20278     },
20279
20280     // private
20281     deactivate : function(){
20282         this.fireEvent("deactivate", this);
20283     },
20284
20285     // private
20286     disable : function(){
20287         this.component.disable();
20288         Roo.menu.Adapter.superclass.disable.call(this);
20289     },
20290
20291     // private
20292     enable : function(){
20293         this.component.enable();
20294         Roo.menu.Adapter.superclass.enable.call(this);
20295     }
20296 });/*
20297  * Based on:
20298  * Ext JS Library 1.1.1
20299  * Copyright(c) 2006-2007, Ext JS, LLC.
20300  *
20301  * Originally Released Under LGPL - original licence link has changed is not relivant.
20302  *
20303  * Fork - LGPL
20304  * <script type="text/javascript">
20305  */
20306
20307 /**
20308  * @class Roo.menu.TextItem
20309  * @extends Roo.menu.BaseItem
20310  * Adds a static text string to a menu, usually used as either a heading or group separator.
20311  * Note: old style constructor with text is still supported.
20312  * 
20313  * @constructor
20314  * Creates a new TextItem
20315  * @param {Object} cfg Configuration
20316  */
20317 Roo.menu.TextItem = function(cfg){
20318     if (typeof(cfg) == 'string') {
20319         this.text = cfg;
20320     } else {
20321         Roo.apply(this,cfg);
20322     }
20323     
20324     Roo.menu.TextItem.superclass.constructor.call(this);
20325 };
20326
20327 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20328     /**
20329      * @cfg {Boolean} text Text to show on item.
20330      */
20331     text : '',
20332     
20333     /**
20334      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20335      */
20336     hideOnClick : false,
20337     /**
20338      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20339      */
20340     itemCls : "x-menu-text",
20341
20342     // private
20343     onRender : function(){
20344         var s = document.createElement("span");
20345         s.className = this.itemCls;
20346         s.innerHTML = this.text;
20347         this.el = s;
20348         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20349     }
20350 });/*
20351  * Based on:
20352  * Ext JS Library 1.1.1
20353  * Copyright(c) 2006-2007, Ext JS, LLC.
20354  *
20355  * Originally Released Under LGPL - original licence link has changed is not relivant.
20356  *
20357  * Fork - LGPL
20358  * <script type="text/javascript">
20359  */
20360
20361 /**
20362  * @class Roo.menu.Separator
20363  * @extends Roo.menu.BaseItem
20364  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20365  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20366  * @constructor
20367  * @param {Object} config Configuration options
20368  */
20369 Roo.menu.Separator = function(config){
20370     Roo.menu.Separator.superclass.constructor.call(this, config);
20371 };
20372
20373 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20374     /**
20375      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20376      */
20377     itemCls : "x-menu-sep",
20378     /**
20379      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20380      */
20381     hideOnClick : false,
20382
20383     // private
20384     onRender : function(li){
20385         var s = document.createElement("span");
20386         s.className = this.itemCls;
20387         s.innerHTML = "&#160;";
20388         this.el = s;
20389         li.addClass("x-menu-sep-li");
20390         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20391     }
20392 });/*
20393  * Based on:
20394  * Ext JS Library 1.1.1
20395  * Copyright(c) 2006-2007, Ext JS, LLC.
20396  *
20397  * Originally Released Under LGPL - original licence link has changed is not relivant.
20398  *
20399  * Fork - LGPL
20400  * <script type="text/javascript">
20401  */
20402 /**
20403  * @class Roo.menu.Item
20404  * @extends Roo.menu.BaseItem
20405  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20406  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20407  * activation and click handling.
20408  * @constructor
20409  * Creates a new Item
20410  * @param {Object} config Configuration options
20411  */
20412 Roo.menu.Item = function(config){
20413     Roo.menu.Item.superclass.constructor.call(this, config);
20414     if(this.menu){
20415         this.menu = Roo.menu.MenuMgr.get(this.menu);
20416     }
20417 };
20418 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20419     
20420     /**
20421      * @cfg {String} text
20422      * The text to show on the menu item.
20423      */
20424     text: '',
20425      /**
20426      * @cfg {String} HTML to render in menu
20427      * The text to show on the menu item (HTML version).
20428      */
20429     html: '',
20430     /**
20431      * @cfg {String} icon
20432      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20433      */
20434     icon: undefined,
20435     /**
20436      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20437      */
20438     itemCls : "x-menu-item",
20439     /**
20440      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20441      */
20442     canActivate : true,
20443     /**
20444      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20445      */
20446     showDelay: 200,
20447     // doc'd in BaseItem
20448     hideDelay: 200,
20449
20450     // private
20451     ctype: "Roo.menu.Item",
20452     
20453     // private
20454     onRender : function(container, position){
20455         var el = document.createElement("a");
20456         el.hideFocus = true;
20457         el.unselectable = "on";
20458         el.href = this.href || "#";
20459         if(this.hrefTarget){
20460             el.target = this.hrefTarget;
20461         }
20462         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20463         
20464         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20465         
20466         el.innerHTML = String.format(
20467                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20468                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20469         this.el = el;
20470         Roo.menu.Item.superclass.onRender.call(this, container, position);
20471     },
20472
20473     /**
20474      * Sets the text to display in this menu item
20475      * @param {String} text The text to display
20476      * @param {Boolean} isHTML true to indicate text is pure html.
20477      */
20478     setText : function(text, isHTML){
20479         if (isHTML) {
20480             this.html = text;
20481         } else {
20482             this.text = text;
20483             this.html = '';
20484         }
20485         if(this.rendered){
20486             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20487      
20488             this.el.update(String.format(
20489                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20490                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20491             this.parentMenu.autoWidth();
20492         }
20493     },
20494
20495     // private
20496     handleClick : function(e){
20497         if(!this.href){ // if no link defined, stop the event automatically
20498             e.stopEvent();
20499         }
20500         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20501     },
20502
20503     // private
20504     activate : function(autoExpand){
20505         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20506             this.focus();
20507             if(autoExpand){
20508                 this.expandMenu();
20509             }
20510         }
20511         return true;
20512     },
20513
20514     // private
20515     shouldDeactivate : function(e){
20516         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20517             if(this.menu && this.menu.isVisible()){
20518                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20519             }
20520             return true;
20521         }
20522         return false;
20523     },
20524
20525     // private
20526     deactivate : function(){
20527         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20528         this.hideMenu();
20529     },
20530
20531     // private
20532     expandMenu : function(autoActivate){
20533         if(!this.disabled && this.menu){
20534             clearTimeout(this.hideTimer);
20535             delete this.hideTimer;
20536             if(!this.menu.isVisible() && !this.showTimer){
20537                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20538             }else if (this.menu.isVisible() && autoActivate){
20539                 this.menu.tryActivate(0, 1);
20540             }
20541         }
20542     },
20543
20544     // private
20545     deferExpand : function(autoActivate){
20546         delete this.showTimer;
20547         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20548         if(autoActivate){
20549             this.menu.tryActivate(0, 1);
20550         }
20551     },
20552
20553     // private
20554     hideMenu : function(){
20555         clearTimeout(this.showTimer);
20556         delete this.showTimer;
20557         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20558             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20559         }
20560     },
20561
20562     // private
20563     deferHide : function(){
20564         delete this.hideTimer;
20565         this.menu.hide();
20566     }
20567 });/*
20568  * Based on:
20569  * Ext JS Library 1.1.1
20570  * Copyright(c) 2006-2007, Ext JS, LLC.
20571  *
20572  * Originally Released Under LGPL - original licence link has changed is not relivant.
20573  *
20574  * Fork - LGPL
20575  * <script type="text/javascript">
20576  */
20577  
20578 /**
20579  * @class Roo.menu.CheckItem
20580  * @extends Roo.menu.Item
20581  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20582  * @constructor
20583  * Creates a new CheckItem
20584  * @param {Object} config Configuration options
20585  */
20586 Roo.menu.CheckItem = function(config){
20587     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20588     this.addEvents({
20589         /**
20590          * @event beforecheckchange
20591          * Fires before the checked value is set, providing an opportunity to cancel if needed
20592          * @param {Roo.menu.CheckItem} this
20593          * @param {Boolean} checked The new checked value that will be set
20594          */
20595         "beforecheckchange" : true,
20596         /**
20597          * @event checkchange
20598          * Fires after the checked value has been set
20599          * @param {Roo.menu.CheckItem} this
20600          * @param {Boolean} checked The checked value that was set
20601          */
20602         "checkchange" : true
20603     });
20604     if(this.checkHandler){
20605         this.on('checkchange', this.checkHandler, this.scope);
20606     }
20607 };
20608 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20609     /**
20610      * @cfg {String} group
20611      * All check items with the same group name will automatically be grouped into a single-select
20612      * radio button group (defaults to '')
20613      */
20614     /**
20615      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20616      */
20617     itemCls : "x-menu-item x-menu-check-item",
20618     /**
20619      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20620      */
20621     groupClass : "x-menu-group-item",
20622
20623     /**
20624      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20625      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20626      * initialized with checked = true will be rendered as checked.
20627      */
20628     checked: false,
20629
20630     // private
20631     ctype: "Roo.menu.CheckItem",
20632
20633     // private
20634     onRender : function(c){
20635         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20636         if(this.group){
20637             this.el.addClass(this.groupClass);
20638         }
20639         Roo.menu.MenuMgr.registerCheckable(this);
20640         if(this.checked){
20641             this.checked = false;
20642             this.setChecked(true, true);
20643         }
20644     },
20645
20646     // private
20647     destroy : function(){
20648         if(this.rendered){
20649             Roo.menu.MenuMgr.unregisterCheckable(this);
20650         }
20651         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20652     },
20653
20654     /**
20655      * Set the checked state of this item
20656      * @param {Boolean} checked The new checked value
20657      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20658      */
20659     setChecked : function(state, suppressEvent){
20660         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20661             if(this.container){
20662                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20663             }
20664             this.checked = state;
20665             if(suppressEvent !== true){
20666                 this.fireEvent("checkchange", this, state);
20667             }
20668         }
20669     },
20670
20671     // private
20672     handleClick : function(e){
20673        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20674            this.setChecked(!this.checked);
20675        }
20676        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20677     }
20678 });/*
20679  * Based on:
20680  * Ext JS Library 1.1.1
20681  * Copyright(c) 2006-2007, Ext JS, LLC.
20682  *
20683  * Originally Released Under LGPL - original licence link has changed is not relivant.
20684  *
20685  * Fork - LGPL
20686  * <script type="text/javascript">
20687  */
20688  
20689 /**
20690  * @class Roo.menu.DateItem
20691  * @extends Roo.menu.Adapter
20692  * A menu item that wraps the {@link Roo.DatPicker} component.
20693  * @constructor
20694  * Creates a new DateItem
20695  * @param {Object} config Configuration options
20696  */
20697 Roo.menu.DateItem = function(config){
20698     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20699     /** The Roo.DatePicker object @type Roo.DatePicker */
20700     this.picker = this.component;
20701     this.addEvents({select: true});
20702     
20703     this.picker.on("render", function(picker){
20704         picker.getEl().swallowEvent("click");
20705         picker.container.addClass("x-menu-date-item");
20706     });
20707
20708     this.picker.on("select", this.onSelect, this);
20709 };
20710
20711 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20712     // private
20713     onSelect : function(picker, date){
20714         this.fireEvent("select", this, date, picker);
20715         Roo.menu.DateItem.superclass.handleClick.call(this);
20716     }
20717 });/*
20718  * Based on:
20719  * Ext JS Library 1.1.1
20720  * Copyright(c) 2006-2007, Ext JS, LLC.
20721  *
20722  * Originally Released Under LGPL - original licence link has changed is not relivant.
20723  *
20724  * Fork - LGPL
20725  * <script type="text/javascript">
20726  */
20727  
20728 /**
20729  * @class Roo.menu.ColorItem
20730  * @extends Roo.menu.Adapter
20731  * A menu item that wraps the {@link Roo.ColorPalette} component.
20732  * @constructor
20733  * Creates a new ColorItem
20734  * @param {Object} config Configuration options
20735  */
20736 Roo.menu.ColorItem = function(config){
20737     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20738     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20739     this.palette = this.component;
20740     this.relayEvents(this.palette, ["select"]);
20741     if(this.selectHandler){
20742         this.on('select', this.selectHandler, this.scope);
20743     }
20744 };
20745 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20746  * Based on:
20747  * Ext JS Library 1.1.1
20748  * Copyright(c) 2006-2007, Ext JS, LLC.
20749  *
20750  * Originally Released Under LGPL - original licence link has changed is not relivant.
20751  *
20752  * Fork - LGPL
20753  * <script type="text/javascript">
20754  */
20755  
20756
20757 /**
20758  * @class Roo.menu.DateMenu
20759  * @extends Roo.menu.Menu
20760  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20761  * @constructor
20762  * Creates a new DateMenu
20763  * @param {Object} config Configuration options
20764  */
20765 Roo.menu.DateMenu = function(config){
20766     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20767     this.plain = true;
20768     var di = new Roo.menu.DateItem(config);
20769     this.add(di);
20770     /**
20771      * The {@link Roo.DatePicker} instance for this DateMenu
20772      * @type DatePicker
20773      */
20774     this.picker = di.picker;
20775     /**
20776      * @event select
20777      * @param {DatePicker} picker
20778      * @param {Date} date
20779      */
20780     this.relayEvents(di, ["select"]);
20781
20782     this.on('beforeshow', function(){
20783         if(this.picker){
20784             this.picker.hideMonthPicker(true);
20785         }
20786     }, this);
20787 };
20788 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20789     cls:'x-date-menu'
20790 });/*
20791  * Based on:
20792  * Ext JS Library 1.1.1
20793  * Copyright(c) 2006-2007, Ext JS, LLC.
20794  *
20795  * Originally Released Under LGPL - original licence link has changed is not relivant.
20796  *
20797  * Fork - LGPL
20798  * <script type="text/javascript">
20799  */
20800  
20801
20802 /**
20803  * @class Roo.menu.ColorMenu
20804  * @extends Roo.menu.Menu
20805  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20806  * @constructor
20807  * Creates a new ColorMenu
20808  * @param {Object} config Configuration options
20809  */
20810 Roo.menu.ColorMenu = function(config){
20811     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20812     this.plain = true;
20813     var ci = new Roo.menu.ColorItem(config);
20814     this.add(ci);
20815     /**
20816      * The {@link Roo.ColorPalette} instance for this ColorMenu
20817      * @type ColorPalette
20818      */
20819     this.palette = ci.palette;
20820     /**
20821      * @event select
20822      * @param {ColorPalette} palette
20823      * @param {String} color
20824      */
20825     this.relayEvents(ci, ["select"]);
20826 };
20827 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20828  * Based on:
20829  * Ext JS Library 1.1.1
20830  * Copyright(c) 2006-2007, Ext JS, LLC.
20831  *
20832  * Originally Released Under LGPL - original licence link has changed is not relivant.
20833  *
20834  * Fork - LGPL
20835  * <script type="text/javascript">
20836  */
20837  
20838 /**
20839  * @class Roo.form.Field
20840  * @extends Roo.BoxComponent
20841  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20842  * @constructor
20843  * Creates a new Field
20844  * @param {Object} config Configuration options
20845  */
20846 Roo.form.Field = function(config){
20847     Roo.form.Field.superclass.constructor.call(this, config);
20848 };
20849
20850 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20851     /**
20852      * @cfg {String} fieldLabel Label to use when rendering a form.
20853      */
20854        /**
20855      * @cfg {String} qtip Mouse over tip
20856      */
20857      
20858     /**
20859      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20860      */
20861     invalidClass : "x-form-invalid",
20862     /**
20863      * @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")
20864      */
20865     invalidText : "The value in this field is invalid",
20866     /**
20867      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20868      */
20869     focusClass : "x-form-focus",
20870     /**
20871      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20872       automatic validation (defaults to "keyup").
20873      */
20874     validationEvent : "keyup",
20875     /**
20876      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20877      */
20878     validateOnBlur : true,
20879     /**
20880      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20881      */
20882     validationDelay : 250,
20883     /**
20884      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20885      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20886      */
20887     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20888     /**
20889      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20890      */
20891     fieldClass : "x-form-field",
20892     /**
20893      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20894      *<pre>
20895 Value         Description
20896 -----------   ----------------------------------------------------------------------
20897 qtip          Display a quick tip when the user hovers over the field
20898 title         Display a default browser title attribute popup
20899 under         Add a block div beneath the field containing the error text
20900 side          Add an error icon to the right of the field with a popup on hover
20901 [element id]  Add the error text directly to the innerHTML of the specified element
20902 </pre>
20903      */
20904     msgTarget : 'qtip',
20905     /**
20906      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20907      */
20908     msgFx : 'normal',
20909
20910     /**
20911      * @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.
20912      */
20913     readOnly : false,
20914
20915     /**
20916      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20917      */
20918     disabled : false,
20919
20920     /**
20921      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20922      */
20923     inputType : undefined,
20924     
20925     /**
20926      * @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).
20927          */
20928         tabIndex : undefined,
20929         
20930     // private
20931     isFormField : true,
20932
20933     // private
20934     hasFocus : false,
20935     /**
20936      * @property {Roo.Element} fieldEl
20937      * Element Containing the rendered Field (with label etc.)
20938      */
20939     /**
20940      * @cfg {Mixed} value A value to initialize this field with.
20941      */
20942     value : undefined,
20943
20944     /**
20945      * @cfg {String} name The field's HTML name attribute.
20946      */
20947     /**
20948      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20949      */
20950
20951         // private ??
20952         initComponent : function(){
20953         Roo.form.Field.superclass.initComponent.call(this);
20954         this.addEvents({
20955             /**
20956              * @event focus
20957              * Fires when this field receives input focus.
20958              * @param {Roo.form.Field} this
20959              */
20960             focus : true,
20961             /**
20962              * @event blur
20963              * Fires when this field loses input focus.
20964              * @param {Roo.form.Field} this
20965              */
20966             blur : true,
20967             /**
20968              * @event specialkey
20969              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20970              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20971              * @param {Roo.form.Field} this
20972              * @param {Roo.EventObject} e The event object
20973              */
20974             specialkey : true,
20975             /**
20976              * @event change
20977              * Fires just before the field blurs if the field value has changed.
20978              * @param {Roo.form.Field} this
20979              * @param {Mixed} newValue The new value
20980              * @param {Mixed} oldValue The original value
20981              */
20982             change : true,
20983             /**
20984              * @event invalid
20985              * Fires after the field has been marked as invalid.
20986              * @param {Roo.form.Field} this
20987              * @param {String} msg The validation message
20988              */
20989             invalid : true,
20990             /**
20991              * @event valid
20992              * Fires after the field has been validated with no errors.
20993              * @param {Roo.form.Field} this
20994              */
20995             valid : true,
20996              /**
20997              * @event keyup
20998              * Fires after the key up
20999              * @param {Roo.form.Field} this
21000              * @param {Roo.EventObject}  e The event Object
21001              */
21002             keyup : true
21003         });
21004     },
21005
21006     /**
21007      * Returns the name attribute of the field if available
21008      * @return {String} name The field name
21009      */
21010     getName: function(){
21011          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21012     },
21013
21014     // private
21015     onRender : function(ct, position){
21016         Roo.form.Field.superclass.onRender.call(this, ct, position);
21017         if(!this.el){
21018             var cfg = this.getAutoCreate();
21019             if(!cfg.name){
21020                 cfg.name = this.name || this.id;
21021             }
21022             if(this.inputType){
21023                 cfg.type = this.inputType;
21024             }
21025             this.el = ct.createChild(cfg, position);
21026         }
21027         var type = this.el.dom.type;
21028         if(type){
21029             if(type == 'password'){
21030                 type = 'text';
21031             }
21032             this.el.addClass('x-form-'+type);
21033         }
21034         if(this.readOnly){
21035             this.el.dom.readOnly = true;
21036         }
21037         if(this.tabIndex !== undefined){
21038             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21039         }
21040
21041         this.el.addClass([this.fieldClass, this.cls]);
21042         this.initValue();
21043     },
21044
21045     /**
21046      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21047      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21048      * @return {Roo.form.Field} this
21049      */
21050     applyTo : function(target){
21051         this.allowDomMove = false;
21052         this.el = Roo.get(target);
21053         this.render(this.el.dom.parentNode);
21054         return this;
21055     },
21056
21057     // private
21058     initValue : function(){
21059         if(this.value !== undefined){
21060             this.setValue(this.value);
21061         }else if(this.el.dom.value.length > 0){
21062             this.setValue(this.el.dom.value);
21063         }
21064     },
21065
21066     /**
21067      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21068      */
21069     isDirty : function() {
21070         if(this.disabled) {
21071             return false;
21072         }
21073         return String(this.getValue()) !== String(this.originalValue);
21074     },
21075
21076     // private
21077     afterRender : function(){
21078         Roo.form.Field.superclass.afterRender.call(this);
21079         this.initEvents();
21080     },
21081
21082     // private
21083     fireKey : function(e){
21084         //Roo.log('field ' + e.getKey());
21085         if(e.isNavKeyPress()){
21086             this.fireEvent("specialkey", this, e);
21087         }
21088     },
21089
21090     /**
21091      * Resets the current field value to the originally loaded value and clears any validation messages
21092      */
21093     reset : function(){
21094         this.setValue(this.originalValue);
21095         this.clearInvalid();
21096     },
21097
21098     // private
21099     initEvents : function(){
21100         // safari killled keypress - so keydown is now used..
21101         this.el.on("keydown" , this.fireKey,  this);
21102         this.el.on("focus", this.onFocus,  this);
21103         this.el.on("blur", this.onBlur,  this);
21104         this.el.relayEvent('keyup', this);
21105
21106         // reference to original value for reset
21107         this.originalValue = this.getValue();
21108     },
21109
21110     // private
21111     onFocus : function(){
21112         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21113             this.el.addClass(this.focusClass);
21114         }
21115         if(!this.hasFocus){
21116             this.hasFocus = true;
21117             this.startValue = this.getValue();
21118             this.fireEvent("focus", this);
21119         }
21120     },
21121
21122     beforeBlur : Roo.emptyFn,
21123
21124     // private
21125     onBlur : function(){
21126         this.beforeBlur();
21127         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21128             this.el.removeClass(this.focusClass);
21129         }
21130         this.hasFocus = false;
21131         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21132             this.validate();
21133         }
21134         var v = this.getValue();
21135         if(String(v) !== String(this.startValue)){
21136             this.fireEvent('change', this, v, this.startValue);
21137         }
21138         this.fireEvent("blur", this);
21139     },
21140
21141     /**
21142      * Returns whether or not the field value is currently valid
21143      * @param {Boolean} preventMark True to disable marking the field invalid
21144      * @return {Boolean} True if the value is valid, else false
21145      */
21146     isValid : function(preventMark){
21147         if(this.disabled){
21148             return true;
21149         }
21150         var restore = this.preventMark;
21151         this.preventMark = preventMark === true;
21152         var v = this.validateValue(this.processValue(this.getRawValue()));
21153         this.preventMark = restore;
21154         return v;
21155     },
21156
21157     /**
21158      * Validates the field value
21159      * @return {Boolean} True if the value is valid, else false
21160      */
21161     validate : function(){
21162         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21163             this.clearInvalid();
21164             return true;
21165         }
21166         return false;
21167     },
21168
21169     processValue : function(value){
21170         return value;
21171     },
21172
21173     // private
21174     // Subclasses should provide the validation implementation by overriding this
21175     validateValue : function(value){
21176         return true;
21177     },
21178
21179     /**
21180      * Mark this field as invalid
21181      * @param {String} msg The validation message
21182      */
21183     markInvalid : function(msg){
21184         if(!this.rendered || this.preventMark){ // not rendered
21185             return;
21186         }
21187         this.el.addClass(this.invalidClass);
21188         msg = msg || this.invalidText;
21189         switch(this.msgTarget){
21190             case 'qtip':
21191                 this.el.dom.qtip = msg;
21192                 this.el.dom.qclass = 'x-form-invalid-tip';
21193                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21194                     Roo.QuickTips.enable();
21195                 }
21196                 break;
21197             case 'title':
21198                 this.el.dom.title = msg;
21199                 break;
21200             case 'under':
21201                 if(!this.errorEl){
21202                     var elp = this.el.findParent('.x-form-element', 5, true);
21203                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21204                     this.errorEl.setWidth(elp.getWidth(true)-20);
21205                 }
21206                 this.errorEl.update(msg);
21207                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21208                 break;
21209             case 'side':
21210                 if(!this.errorIcon){
21211                     var elp = this.el.findParent('.x-form-element', 5, true);
21212                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21213                 }
21214                 this.alignErrorIcon();
21215                 this.errorIcon.dom.qtip = msg;
21216                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21217                 this.errorIcon.show();
21218                 this.on('resize', this.alignErrorIcon, this);
21219                 break;
21220             default:
21221                 var t = Roo.getDom(this.msgTarget);
21222                 t.innerHTML = msg;
21223                 t.style.display = this.msgDisplay;
21224                 break;
21225         }
21226         this.fireEvent('invalid', this, msg);
21227     },
21228
21229     // private
21230     alignErrorIcon : function(){
21231         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21232     },
21233
21234     /**
21235      * Clear any invalid styles/messages for this field
21236      */
21237     clearInvalid : function(){
21238         if(!this.rendered || this.preventMark){ // not rendered
21239             return;
21240         }
21241         this.el.removeClass(this.invalidClass);
21242         switch(this.msgTarget){
21243             case 'qtip':
21244                 this.el.dom.qtip = '';
21245                 break;
21246             case 'title':
21247                 this.el.dom.title = '';
21248                 break;
21249             case 'under':
21250                 if(this.errorEl){
21251                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21252                 }
21253                 break;
21254             case 'side':
21255                 if(this.errorIcon){
21256                     this.errorIcon.dom.qtip = '';
21257                     this.errorIcon.hide();
21258                     this.un('resize', this.alignErrorIcon, this);
21259                 }
21260                 break;
21261             default:
21262                 var t = Roo.getDom(this.msgTarget);
21263                 t.innerHTML = '';
21264                 t.style.display = 'none';
21265                 break;
21266         }
21267         this.fireEvent('valid', this);
21268     },
21269
21270     /**
21271      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21272      * @return {Mixed} value The field value
21273      */
21274     getRawValue : function(){
21275         var v = this.el.getValue();
21276         if(v === this.emptyText){
21277             v = '';
21278         }
21279         return v;
21280     },
21281
21282     /**
21283      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21284      * @return {Mixed} value The field value
21285      */
21286     getValue : function(){
21287         var v = this.el.getValue();
21288         if(v === this.emptyText || v === undefined){
21289             v = '';
21290         }
21291         return v;
21292     },
21293
21294     /**
21295      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21296      * @param {Mixed} value The value to set
21297      */
21298     setRawValue : function(v){
21299         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21300     },
21301
21302     /**
21303      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21304      * @param {Mixed} value The value to set
21305      */
21306     setValue : function(v){
21307         this.value = v;
21308         if(this.rendered){
21309             this.el.dom.value = (v === null || v === undefined ? '' : v);
21310             this.validate();
21311         }
21312     },
21313
21314     adjustSize : function(w, h){
21315         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21316         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21317         return s;
21318     },
21319
21320     adjustWidth : function(tag, w){
21321         tag = tag.toLowerCase();
21322         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21323             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21324                 if(tag == 'input'){
21325                     return w + 2;
21326                 }
21327                 if(tag = 'textarea'){
21328                     return w-2;
21329                 }
21330             }else if(Roo.isOpera){
21331                 if(tag == 'input'){
21332                     return w + 2;
21333                 }
21334                 if(tag = 'textarea'){
21335                     return w-2;
21336                 }
21337             }
21338         }
21339         return w;
21340     }
21341 });
21342
21343
21344 // anything other than normal should be considered experimental
21345 Roo.form.Field.msgFx = {
21346     normal : {
21347         show: function(msgEl, f){
21348             msgEl.setDisplayed('block');
21349         },
21350
21351         hide : function(msgEl, f){
21352             msgEl.setDisplayed(false).update('');
21353         }
21354     },
21355
21356     slide : {
21357         show: function(msgEl, f){
21358             msgEl.slideIn('t', {stopFx:true});
21359         },
21360
21361         hide : function(msgEl, f){
21362             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21363         }
21364     },
21365
21366     slideRight : {
21367         show: function(msgEl, f){
21368             msgEl.fixDisplay();
21369             msgEl.alignTo(f.el, 'tl-tr');
21370             msgEl.slideIn('l', {stopFx:true});
21371         },
21372
21373         hide : function(msgEl, f){
21374             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21375         }
21376     }
21377 };/*
21378  * Based on:
21379  * Ext JS Library 1.1.1
21380  * Copyright(c) 2006-2007, Ext JS, LLC.
21381  *
21382  * Originally Released Under LGPL - original licence link has changed is not relivant.
21383  *
21384  * Fork - LGPL
21385  * <script type="text/javascript">
21386  */
21387  
21388
21389 /**
21390  * @class Roo.form.TextField
21391  * @extends Roo.form.Field
21392  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21393  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21394  * @constructor
21395  * Creates a new TextField
21396  * @param {Object} config Configuration options
21397  */
21398 Roo.form.TextField = function(config){
21399     Roo.form.TextField.superclass.constructor.call(this, config);
21400     this.addEvents({
21401         /**
21402          * @event autosize
21403          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21404          * according to the default logic, but this event provides a hook for the developer to apply additional
21405          * logic at runtime to resize the field if needed.
21406              * @param {Roo.form.Field} this This text field
21407              * @param {Number} width The new field width
21408              */
21409         autosize : true
21410     });
21411 };
21412
21413 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21414     /**
21415      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21416      */
21417     grow : false,
21418     /**
21419      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21420      */
21421     growMin : 30,
21422     /**
21423      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21424      */
21425     growMax : 800,
21426     /**
21427      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21428      */
21429     vtype : null,
21430     /**
21431      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21432      */
21433     maskRe : null,
21434     /**
21435      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21436      */
21437     disableKeyFilter : false,
21438     /**
21439      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21440      */
21441     allowBlank : true,
21442     /**
21443      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21444      */
21445     minLength : 0,
21446     /**
21447      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21448      */
21449     maxLength : Number.MAX_VALUE,
21450     /**
21451      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21452      */
21453     minLengthText : "The minimum length for this field is {0}",
21454     /**
21455      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21456      */
21457     maxLengthText : "The maximum length for this field is {0}",
21458     /**
21459      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21460      */
21461     selectOnFocus : false,
21462     /**
21463      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21464      */
21465     blankText : "This field is required",
21466     /**
21467      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21468      * If available, this function will be called only after the basic validators all return true, and will be passed the
21469      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21470      */
21471     validator : null,
21472     /**
21473      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21474      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21475      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21476      */
21477     regex : null,
21478     /**
21479      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21480      */
21481     regexText : "",
21482     /**
21483      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21484      */
21485     emptyText : null,
21486     /**
21487      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21488      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21489      */
21490     emptyClass : 'x-form-empty-field',
21491
21492     // private
21493     initEvents : function(){
21494         Roo.form.TextField.superclass.initEvents.call(this);
21495         if(this.validationEvent == 'keyup'){
21496             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21497             this.el.on('keyup', this.filterValidation, this);
21498         }
21499         else if(this.validationEvent !== false){
21500             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21501         }
21502         if(this.selectOnFocus || this.emptyText){
21503             this.on("focus", this.preFocus, this);
21504             if(this.emptyText){
21505                 this.on('blur', this.postBlur, this);
21506                 this.applyEmptyText();
21507             }
21508         }
21509         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21510             this.el.on("keypress", this.filterKeys, this);
21511         }
21512         if(this.grow){
21513             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21514             this.el.on("click", this.autoSize,  this);
21515         }
21516     },
21517
21518     processValue : function(value){
21519         if(this.stripCharsRe){
21520             var newValue = value.replace(this.stripCharsRe, '');
21521             if(newValue !== value){
21522                 this.setRawValue(newValue);
21523                 return newValue;
21524             }
21525         }
21526         return value;
21527     },
21528
21529     filterValidation : function(e){
21530         if(!e.isNavKeyPress()){
21531             this.validationTask.delay(this.validationDelay);
21532         }
21533     },
21534
21535     // private
21536     onKeyUp : function(e){
21537         if(!e.isNavKeyPress()){
21538             this.autoSize();
21539         }
21540     },
21541
21542     /**
21543      * Resets the current field value to the originally-loaded value and clears any validation messages.
21544      * Also adds emptyText and emptyClass if the original value was blank.
21545      */
21546     reset : function(){
21547         Roo.form.TextField.superclass.reset.call(this);
21548         this.applyEmptyText();
21549     },
21550
21551     applyEmptyText : function(){
21552         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21553             this.setRawValue(this.emptyText);
21554             this.el.addClass(this.emptyClass);
21555         }
21556     },
21557
21558     // private
21559     preFocus : function(){
21560         if(this.emptyText){
21561             if(this.el.dom.value == this.emptyText){
21562                 this.setRawValue('');
21563             }
21564             this.el.removeClass(this.emptyClass);
21565         }
21566         if(this.selectOnFocus){
21567             this.el.dom.select();
21568         }
21569     },
21570
21571     // private
21572     postBlur : function(){
21573         this.applyEmptyText();
21574     },
21575
21576     // private
21577     filterKeys : function(e){
21578         var k = e.getKey();
21579         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21580             return;
21581         }
21582         var c = e.getCharCode(), cc = String.fromCharCode(c);
21583         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21584             return;
21585         }
21586         if(!this.maskRe.test(cc)){
21587             e.stopEvent();
21588         }
21589     },
21590
21591     setValue : function(v){
21592         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21593             this.el.removeClass(this.emptyClass);
21594         }
21595         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21596         this.applyEmptyText();
21597         this.autoSize();
21598     },
21599
21600     /**
21601      * Validates a value according to the field's validation rules and marks the field as invalid
21602      * if the validation fails
21603      * @param {Mixed} value The value to validate
21604      * @return {Boolean} True if the value is valid, else false
21605      */
21606     validateValue : function(value){
21607         if(value.length < 1 || value === this.emptyText){ // if it's blank
21608              if(this.allowBlank){
21609                 this.clearInvalid();
21610                 return true;
21611              }else{
21612                 this.markInvalid(this.blankText);
21613                 return false;
21614              }
21615         }
21616         if(value.length < this.minLength){
21617             this.markInvalid(String.format(this.minLengthText, this.minLength));
21618             return false;
21619         }
21620         if(value.length > this.maxLength){
21621             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21622             return false;
21623         }
21624         if(this.vtype){
21625             var vt = Roo.form.VTypes;
21626             if(!vt[this.vtype](value, this)){
21627                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21628                 return false;
21629             }
21630         }
21631         if(typeof this.validator == "function"){
21632             var msg = this.validator(value);
21633             if(msg !== true){
21634                 this.markInvalid(msg);
21635                 return false;
21636             }
21637         }
21638         if(this.regex && !this.regex.test(value)){
21639             this.markInvalid(this.regexText);
21640             return false;
21641         }
21642         return true;
21643     },
21644
21645     /**
21646      * Selects text in this field
21647      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21648      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21649      */
21650     selectText : function(start, end){
21651         var v = this.getRawValue();
21652         if(v.length > 0){
21653             start = start === undefined ? 0 : start;
21654             end = end === undefined ? v.length : end;
21655             var d = this.el.dom;
21656             if(d.setSelectionRange){
21657                 d.setSelectionRange(start, end);
21658             }else if(d.createTextRange){
21659                 var range = d.createTextRange();
21660                 range.moveStart("character", start);
21661                 range.moveEnd("character", v.length-end);
21662                 range.select();
21663             }
21664         }
21665     },
21666
21667     /**
21668      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21669      * This only takes effect if grow = true, and fires the autosize event.
21670      */
21671     autoSize : function(){
21672         if(!this.grow || !this.rendered){
21673             return;
21674         }
21675         if(!this.metrics){
21676             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21677         }
21678         var el = this.el;
21679         var v = el.dom.value;
21680         var d = document.createElement('div');
21681         d.appendChild(document.createTextNode(v));
21682         v = d.innerHTML;
21683         d = null;
21684         v += "&#160;";
21685         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21686         this.el.setWidth(w);
21687         this.fireEvent("autosize", this, w);
21688     }
21689 });/*
21690  * Based on:
21691  * Ext JS Library 1.1.1
21692  * Copyright(c) 2006-2007, Ext JS, LLC.
21693  *
21694  * Originally Released Under LGPL - original licence link has changed is not relivant.
21695  *
21696  * Fork - LGPL
21697  * <script type="text/javascript">
21698  */
21699  
21700 /**
21701  * @class Roo.form.Hidden
21702  * @extends Roo.form.TextField
21703  * Simple Hidden element used on forms 
21704  * 
21705  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21706  * 
21707  * @constructor
21708  * Creates a new Hidden form element.
21709  * @param {Object} config Configuration options
21710  */
21711
21712
21713
21714 // easy hidden field...
21715 Roo.form.Hidden = function(config){
21716     Roo.form.Hidden.superclass.constructor.call(this, config);
21717 };
21718   
21719 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21720     fieldLabel:      '',
21721     inputType:      'hidden',
21722     width:          50,
21723     allowBlank:     true,
21724     labelSeparator: '',
21725     hidden:         true,
21726     itemCls :       'x-form-item-display-none'
21727
21728
21729 });
21730
21731
21732 /*
21733  * Based on:
21734  * Ext JS Library 1.1.1
21735  * Copyright(c) 2006-2007, Ext JS, LLC.
21736  *
21737  * Originally Released Under LGPL - original licence link has changed is not relivant.
21738  *
21739  * Fork - LGPL
21740  * <script type="text/javascript">
21741  */
21742  
21743 /**
21744  * @class Roo.form.TriggerField
21745  * @extends Roo.form.TextField
21746  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21747  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21748  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21749  * for which you can provide a custom implementation.  For example:
21750  * <pre><code>
21751 var trigger = new Roo.form.TriggerField();
21752 trigger.onTriggerClick = myTriggerFn;
21753 trigger.applyTo('my-field');
21754 </code></pre>
21755  *
21756  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21757  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21758  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21759  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21760  * @constructor
21761  * Create a new TriggerField.
21762  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21763  * to the base TextField)
21764  */
21765 Roo.form.TriggerField = function(config){
21766     this.mimicing = false;
21767     Roo.form.TriggerField.superclass.constructor.call(this, config);
21768 };
21769
21770 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21771     /**
21772      * @cfg {String} triggerClass A CSS class to apply to the trigger
21773      */
21774     /**
21775      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21776      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21777      */
21778     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21779     /**
21780      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21781      */
21782     hideTrigger:false,
21783
21784     /** @cfg {Boolean} grow @hide */
21785     /** @cfg {Number} growMin @hide */
21786     /** @cfg {Number} growMax @hide */
21787
21788     /**
21789      * @hide 
21790      * @method
21791      */
21792     autoSize: Roo.emptyFn,
21793     // private
21794     monitorTab : true,
21795     // private
21796     deferHeight : true,
21797
21798     
21799     actionMode : 'wrap',
21800     // private
21801     onResize : function(w, h){
21802         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21803         if(typeof w == 'number'){
21804             var x = w - this.trigger.getWidth();
21805             this.el.setWidth(this.adjustWidth('input', x));
21806             this.trigger.setStyle('left', x+'px');
21807         }
21808     },
21809
21810     // private
21811     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21812
21813     // private
21814     getResizeEl : function(){
21815         return this.wrap;
21816     },
21817
21818     // private
21819     getPositionEl : function(){
21820         return this.wrap;
21821     },
21822
21823     // private
21824     alignErrorIcon : function(){
21825         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21826     },
21827
21828     // private
21829     onRender : function(ct, position){
21830         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21831         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21832         this.trigger = this.wrap.createChild(this.triggerConfig ||
21833                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21834         if(this.hideTrigger){
21835             this.trigger.setDisplayed(false);
21836         }
21837         this.initTrigger();
21838         if(!this.width){
21839             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21840         }
21841     },
21842
21843     // private
21844     initTrigger : function(){
21845         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21846         this.trigger.addClassOnOver('x-form-trigger-over');
21847         this.trigger.addClassOnClick('x-form-trigger-click');
21848     },
21849
21850     // private
21851     onDestroy : function(){
21852         if(this.trigger){
21853             this.trigger.removeAllListeners();
21854             this.trigger.remove();
21855         }
21856         if(this.wrap){
21857             this.wrap.remove();
21858         }
21859         Roo.form.TriggerField.superclass.onDestroy.call(this);
21860     },
21861
21862     // private
21863     onFocus : function(){
21864         Roo.form.TriggerField.superclass.onFocus.call(this);
21865         if(!this.mimicing){
21866             this.wrap.addClass('x-trigger-wrap-focus');
21867             this.mimicing = true;
21868             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21869             if(this.monitorTab){
21870                 this.el.on("keydown", this.checkTab, this);
21871             }
21872         }
21873     },
21874
21875     // private
21876     checkTab : function(e){
21877         if(e.getKey() == e.TAB){
21878             this.triggerBlur();
21879         }
21880     },
21881
21882     // private
21883     onBlur : function(){
21884         // do nothing
21885     },
21886
21887     // private
21888     mimicBlur : function(e, t){
21889         if(!this.wrap.contains(t) && this.validateBlur()){
21890             this.triggerBlur();
21891         }
21892     },
21893
21894     // private
21895     triggerBlur : function(){
21896         this.mimicing = false;
21897         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21898         if(this.monitorTab){
21899             this.el.un("keydown", this.checkTab, this);
21900         }
21901         this.wrap.removeClass('x-trigger-wrap-focus');
21902         Roo.form.TriggerField.superclass.onBlur.call(this);
21903     },
21904
21905     // private
21906     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21907     validateBlur : function(e, t){
21908         return true;
21909     },
21910
21911     // private
21912     onDisable : function(){
21913         Roo.form.TriggerField.superclass.onDisable.call(this);
21914         if(this.wrap){
21915             this.wrap.addClass('x-item-disabled');
21916         }
21917     },
21918
21919     // private
21920     onEnable : function(){
21921         Roo.form.TriggerField.superclass.onEnable.call(this);
21922         if(this.wrap){
21923             this.wrap.removeClass('x-item-disabled');
21924         }
21925     },
21926
21927     // private
21928     onShow : function(){
21929         var ae = this.getActionEl();
21930         
21931         if(ae){
21932             ae.dom.style.display = '';
21933             ae.dom.style.visibility = 'visible';
21934         }
21935     },
21936
21937     // private
21938     
21939     onHide : function(){
21940         var ae = this.getActionEl();
21941         ae.dom.style.display = 'none';
21942     },
21943
21944     /**
21945      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21946      * by an implementing function.
21947      * @method
21948      * @param {EventObject} e
21949      */
21950     onTriggerClick : Roo.emptyFn
21951 });
21952
21953 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21954 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21955 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21956 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21957     initComponent : function(){
21958         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21959
21960         this.triggerConfig = {
21961             tag:'span', cls:'x-form-twin-triggers', cn:[
21962             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21963             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21964         ]};
21965     },
21966
21967     getTrigger : function(index){
21968         return this.triggers[index];
21969     },
21970
21971     initTrigger : function(){
21972         var ts = this.trigger.select('.x-form-trigger', true);
21973         this.wrap.setStyle('overflow', 'hidden');
21974         var triggerField = this;
21975         ts.each(function(t, all, index){
21976             t.hide = function(){
21977                 var w = triggerField.wrap.getWidth();
21978                 this.dom.style.display = 'none';
21979                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21980             };
21981             t.show = function(){
21982                 var w = triggerField.wrap.getWidth();
21983                 this.dom.style.display = '';
21984                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21985             };
21986             var triggerIndex = 'Trigger'+(index+1);
21987
21988             if(this['hide'+triggerIndex]){
21989                 t.dom.style.display = 'none';
21990             }
21991             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21992             t.addClassOnOver('x-form-trigger-over');
21993             t.addClassOnClick('x-form-trigger-click');
21994         }, this);
21995         this.triggers = ts.elements;
21996     },
21997
21998     onTrigger1Click : Roo.emptyFn,
21999     onTrigger2Click : Roo.emptyFn
22000 });/*
22001  * Based on:
22002  * Ext JS Library 1.1.1
22003  * Copyright(c) 2006-2007, Ext JS, LLC.
22004  *
22005  * Originally Released Under LGPL - original licence link has changed is not relivant.
22006  *
22007  * Fork - LGPL
22008  * <script type="text/javascript">
22009  */
22010  
22011 /**
22012  * @class Roo.form.TextArea
22013  * @extends Roo.form.TextField
22014  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22015  * support for auto-sizing.
22016  * @constructor
22017  * Creates a new TextArea
22018  * @param {Object} config Configuration options
22019  */
22020 Roo.form.TextArea = function(config){
22021     Roo.form.TextArea.superclass.constructor.call(this, config);
22022     // these are provided exchanges for backwards compat
22023     // minHeight/maxHeight were replaced by growMin/growMax to be
22024     // compatible with TextField growing config values
22025     if(this.minHeight !== undefined){
22026         this.growMin = this.minHeight;
22027     }
22028     if(this.maxHeight !== undefined){
22029         this.growMax = this.maxHeight;
22030     }
22031 };
22032
22033 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22034     /**
22035      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22036      */
22037     growMin : 60,
22038     /**
22039      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22040      */
22041     growMax: 1000,
22042     /**
22043      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22044      * in the field (equivalent to setting overflow: hidden, defaults to false)
22045      */
22046     preventScrollbars: false,
22047     /**
22048      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22049      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22050      */
22051
22052     // private
22053     onRender : function(ct, position){
22054         if(!this.el){
22055             this.defaultAutoCreate = {
22056                 tag: "textarea",
22057                 style:"width:300px;height:60px;",
22058                 autocomplete: "off"
22059             };
22060         }
22061         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22062         if(this.grow){
22063             this.textSizeEl = Roo.DomHelper.append(document.body, {
22064                 tag: "pre", cls: "x-form-grow-sizer"
22065             });
22066             if(this.preventScrollbars){
22067                 this.el.setStyle("overflow", "hidden");
22068             }
22069             this.el.setHeight(this.growMin);
22070         }
22071     },
22072
22073     onDestroy : function(){
22074         if(this.textSizeEl){
22075             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22076         }
22077         Roo.form.TextArea.superclass.onDestroy.call(this);
22078     },
22079
22080     // private
22081     onKeyUp : function(e){
22082         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22083             this.autoSize();
22084         }
22085     },
22086
22087     /**
22088      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22089      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22090      */
22091     autoSize : function(){
22092         if(!this.grow || !this.textSizeEl){
22093             return;
22094         }
22095         var el = this.el;
22096         var v = el.dom.value;
22097         var ts = this.textSizeEl;
22098
22099         ts.innerHTML = '';
22100         ts.appendChild(document.createTextNode(v));
22101         v = ts.innerHTML;
22102
22103         Roo.fly(ts).setWidth(this.el.getWidth());
22104         if(v.length < 1){
22105             v = "&#160;&#160;";
22106         }else{
22107             if(Roo.isIE){
22108                 v = v.replace(/\n/g, '<p>&#160;</p>');
22109             }
22110             v += "&#160;\n&#160;";
22111         }
22112         ts.innerHTML = v;
22113         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22114         if(h != this.lastHeight){
22115             this.lastHeight = h;
22116             this.el.setHeight(h);
22117             this.fireEvent("autosize", this, h);
22118         }
22119     }
22120 });/*
22121  * Based on:
22122  * Ext JS Library 1.1.1
22123  * Copyright(c) 2006-2007, Ext JS, LLC.
22124  *
22125  * Originally Released Under LGPL - original licence link has changed is not relivant.
22126  *
22127  * Fork - LGPL
22128  * <script type="text/javascript">
22129  */
22130  
22131
22132 /**
22133  * @class Roo.form.NumberField
22134  * @extends Roo.form.TextField
22135  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22136  * @constructor
22137  * Creates a new NumberField
22138  * @param {Object} config Configuration options
22139  */
22140 Roo.form.NumberField = function(config){
22141     Roo.form.NumberField.superclass.constructor.call(this, config);
22142 };
22143
22144 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22145     /**
22146      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22147      */
22148     fieldClass: "x-form-field x-form-num-field",
22149     /**
22150      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22151      */
22152     allowDecimals : true,
22153     /**
22154      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22155      */
22156     decimalSeparator : ".",
22157     /**
22158      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22159      */
22160     decimalPrecision : 2,
22161     /**
22162      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22163      */
22164     allowNegative : true,
22165     /**
22166      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22167      */
22168     minValue : Number.NEGATIVE_INFINITY,
22169     /**
22170      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22171      */
22172     maxValue : Number.MAX_VALUE,
22173     /**
22174      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22175      */
22176     minText : "The minimum value for this field is {0}",
22177     /**
22178      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22179      */
22180     maxText : "The maximum value for this field is {0}",
22181     /**
22182      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22183      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22184      */
22185     nanText : "{0} is not a valid number",
22186
22187     // private
22188     initEvents : function(){
22189         Roo.form.NumberField.superclass.initEvents.call(this);
22190         var allowed = "0123456789";
22191         if(this.allowDecimals){
22192             allowed += this.decimalSeparator;
22193         }
22194         if(this.allowNegative){
22195             allowed += "-";
22196         }
22197         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22198         var keyPress = function(e){
22199             var k = e.getKey();
22200             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22201                 return;
22202             }
22203             var c = e.getCharCode();
22204             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22205                 e.stopEvent();
22206             }
22207         };
22208         this.el.on("keypress", keyPress, this);
22209     },
22210
22211     // private
22212     validateValue : function(value){
22213         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22214             return false;
22215         }
22216         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22217              return true;
22218         }
22219         var num = this.parseValue(value);
22220         if(isNaN(num)){
22221             this.markInvalid(String.format(this.nanText, value));
22222             return false;
22223         }
22224         if(num < this.minValue){
22225             this.markInvalid(String.format(this.minText, this.minValue));
22226             return false;
22227         }
22228         if(num > this.maxValue){
22229             this.markInvalid(String.format(this.maxText, this.maxValue));
22230             return false;
22231         }
22232         return true;
22233     },
22234
22235     getValue : function(){
22236         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22237     },
22238
22239     // private
22240     parseValue : function(value){
22241         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22242         return isNaN(value) ? '' : value;
22243     },
22244
22245     // private
22246     fixPrecision : function(value){
22247         var nan = isNaN(value);
22248         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22249             return nan ? '' : value;
22250         }
22251         return parseFloat(value).toFixed(this.decimalPrecision);
22252     },
22253
22254     setValue : function(v){
22255         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22256     },
22257
22258     // private
22259     decimalPrecisionFcn : function(v){
22260         return Math.floor(v);
22261     },
22262
22263     beforeBlur : function(){
22264         var v = this.parseValue(this.getRawValue());
22265         if(v){
22266             this.setValue(this.fixPrecision(v));
22267         }
22268     }
22269 });/*
22270  * Based on:
22271  * Ext JS Library 1.1.1
22272  * Copyright(c) 2006-2007, Ext JS, LLC.
22273  *
22274  * Originally Released Under LGPL - original licence link has changed is not relivant.
22275  *
22276  * Fork - LGPL
22277  * <script type="text/javascript">
22278  */
22279  
22280 /**
22281  * @class Roo.form.DateField
22282  * @extends Roo.form.TriggerField
22283  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22284 * @constructor
22285 * Create a new DateField
22286 * @param {Object} config
22287  */
22288 Roo.form.DateField = function(config){
22289     Roo.form.DateField.superclass.constructor.call(this, config);
22290     
22291       this.addEvents({
22292          
22293         /**
22294          * @event select
22295          * Fires when a date is selected
22296              * @param {Roo.form.DateField} combo This combo box
22297              * @param {Date} date The date selected
22298              */
22299         'select' : true
22300          
22301     });
22302     
22303     
22304     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22305     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22306     this.ddMatch = null;
22307     if(this.disabledDates){
22308         var dd = this.disabledDates;
22309         var re = "(?:";
22310         for(var i = 0; i < dd.length; i++){
22311             re += dd[i];
22312             if(i != dd.length-1) re += "|";
22313         }
22314         this.ddMatch = new RegExp(re + ")");
22315     }
22316 };
22317
22318 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22319     /**
22320      * @cfg {String} format
22321      * The default date format string which can be overriden for localization support.  The format must be
22322      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22323      */
22324     format : "m/d/y",
22325     /**
22326      * @cfg {String} altFormats
22327      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22328      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22329      */
22330     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22331     /**
22332      * @cfg {Array} disabledDays
22333      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22334      */
22335     disabledDays : null,
22336     /**
22337      * @cfg {String} disabledDaysText
22338      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22339      */
22340     disabledDaysText : "Disabled",
22341     /**
22342      * @cfg {Array} disabledDates
22343      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22344      * expression so they are very powerful. Some examples:
22345      * <ul>
22346      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22347      * <li>["03/08", "09/16"] would disable those days for every year</li>
22348      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22349      * <li>["03/../2006"] would disable every day in March 2006</li>
22350      * <li>["^03"] would disable every day in every March</li>
22351      * </ul>
22352      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22353      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22354      */
22355     disabledDates : null,
22356     /**
22357      * @cfg {String} disabledDatesText
22358      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22359      */
22360     disabledDatesText : "Disabled",
22361     /**
22362      * @cfg {Date/String} minValue
22363      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22364      * valid format (defaults to null).
22365      */
22366     minValue : null,
22367     /**
22368      * @cfg {Date/String} maxValue
22369      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22370      * valid format (defaults to null).
22371      */
22372     maxValue : null,
22373     /**
22374      * @cfg {String} minText
22375      * The error text to display when the date in the cell is before minValue (defaults to
22376      * 'The date in this field must be after {minValue}').
22377      */
22378     minText : "The date in this field must be equal to or after {0}",
22379     /**
22380      * @cfg {String} maxText
22381      * The error text to display when the date in the cell is after maxValue (defaults to
22382      * 'The date in this field must be before {maxValue}').
22383      */
22384     maxText : "The date in this field must be equal to or before {0}",
22385     /**
22386      * @cfg {String} invalidText
22387      * The error text to display when the date in the field is invalid (defaults to
22388      * '{value} is not a valid date - it must be in the format {format}').
22389      */
22390     invalidText : "{0} is not a valid date - it must be in the format {1}",
22391     /**
22392      * @cfg {String} triggerClass
22393      * An additional CSS class used to style the trigger button.  The trigger will always get the
22394      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22395      * which displays a calendar icon).
22396      */
22397     triggerClass : 'x-form-date-trigger',
22398     
22399
22400     /**
22401      * @cfg {bool} useIso
22402      * if enabled, then the date field will use a hidden field to store the 
22403      * real value as iso formated date. default (false)
22404      */ 
22405     useIso : false,
22406     /**
22407      * @cfg {String/Object} autoCreate
22408      * A DomHelper element spec, or true for a default element spec (defaults to
22409      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22410      */ 
22411     // private
22412     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22413     
22414     // private
22415     hiddenField: false,
22416     
22417     onRender : function(ct, position)
22418     {
22419         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22420         if (this.useIso) {
22421             this.el.dom.removeAttribute('name'); 
22422             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22423                     'before', true);
22424             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22425             // prevent input submission
22426             this.hiddenName = this.name;
22427         }
22428             
22429             
22430     },
22431     
22432     // private
22433     validateValue : function(value)
22434     {
22435         value = this.formatDate(value);
22436         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22437             return false;
22438         }
22439         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22440              return true;
22441         }
22442         var svalue = value;
22443         value = this.parseDate(value);
22444         if(!value){
22445             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22446             return false;
22447         }
22448         var time = value.getTime();
22449         if(this.minValue && time < this.minValue.getTime()){
22450             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22451             return false;
22452         }
22453         if(this.maxValue && time > this.maxValue.getTime()){
22454             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22455             return false;
22456         }
22457         if(this.disabledDays){
22458             var day = value.getDay();
22459             for(var i = 0; i < this.disabledDays.length; i++) {
22460                 if(day === this.disabledDays[i]){
22461                     this.markInvalid(this.disabledDaysText);
22462                     return false;
22463                 }
22464             }
22465         }
22466         var fvalue = this.formatDate(value);
22467         if(this.ddMatch && this.ddMatch.test(fvalue)){
22468             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22469             return false;
22470         }
22471         return true;
22472     },
22473
22474     // private
22475     // Provides logic to override the default TriggerField.validateBlur which just returns true
22476     validateBlur : function(){
22477         return !this.menu || !this.menu.isVisible();
22478     },
22479
22480     /**
22481      * Returns the current date value of the date field.
22482      * @return {Date} The date value
22483      */
22484     getValue : function(){
22485         
22486         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22487     },
22488
22489     /**
22490      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22491      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22492      * (the default format used is "m/d/y").
22493      * <br />Usage:
22494      * <pre><code>
22495 //All of these calls set the same date value (May 4, 2006)
22496
22497 //Pass a date object:
22498 var dt = new Date('5/4/06');
22499 dateField.setValue(dt);
22500
22501 //Pass a date string (default format):
22502 dateField.setValue('5/4/06');
22503
22504 //Pass a date string (custom format):
22505 dateField.format = 'Y-m-d';
22506 dateField.setValue('2006-5-4');
22507 </code></pre>
22508      * @param {String/Date} date The date or valid date string
22509      */
22510     setValue : function(date){
22511         if (this.hiddenField) {
22512             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22513         }
22514         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22515     },
22516
22517     // private
22518     parseDate : function(value){
22519         if(!value || value instanceof Date){
22520             return value;
22521         }
22522         var v = Date.parseDate(value, this.format);
22523         if(!v && this.altFormats){
22524             if(!this.altFormatsArray){
22525                 this.altFormatsArray = this.altFormats.split("|");
22526             }
22527             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22528                 v = Date.parseDate(value, this.altFormatsArray[i]);
22529             }
22530         }
22531         return v;
22532     },
22533
22534     // private
22535     formatDate : function(date, fmt){
22536         return (!date || !(date instanceof Date)) ?
22537                date : date.dateFormat(fmt || this.format);
22538     },
22539
22540     // private
22541     menuListeners : {
22542         select: function(m, d){
22543             this.setValue(d);
22544             this.fireEvent('select', this, d);
22545         },
22546         show : function(){ // retain focus styling
22547             this.onFocus();
22548         },
22549         hide : function(){
22550             this.focus.defer(10, this);
22551             var ml = this.menuListeners;
22552             this.menu.un("select", ml.select,  this);
22553             this.menu.un("show", ml.show,  this);
22554             this.menu.un("hide", ml.hide,  this);
22555         }
22556     },
22557
22558     // private
22559     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22560     onTriggerClick : function(){
22561         if(this.disabled){
22562             return;
22563         }
22564         if(this.menu == null){
22565             this.menu = new Roo.menu.DateMenu();
22566         }
22567         Roo.apply(this.menu.picker,  {
22568             showClear: this.allowBlank,
22569             minDate : this.minValue,
22570             maxDate : this.maxValue,
22571             disabledDatesRE : this.ddMatch,
22572             disabledDatesText : this.disabledDatesText,
22573             disabledDays : this.disabledDays,
22574             disabledDaysText : this.disabledDaysText,
22575             format : this.format,
22576             minText : String.format(this.minText, this.formatDate(this.minValue)),
22577             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22578         });
22579         this.menu.on(Roo.apply({}, this.menuListeners, {
22580             scope:this
22581         }));
22582         this.menu.picker.setValue(this.getValue() || new Date());
22583         this.menu.show(this.el, "tl-bl?");
22584     },
22585
22586     beforeBlur : function(){
22587         var v = this.parseDate(this.getRawValue());
22588         if(v){
22589             this.setValue(v);
22590         }
22591     }
22592
22593     /** @cfg {Boolean} grow @hide */
22594     /** @cfg {Number} growMin @hide */
22595     /** @cfg {Number} growMax @hide */
22596     /**
22597      * @hide
22598      * @method autoSize
22599      */
22600 });/*
22601  * Based on:
22602  * Ext JS Library 1.1.1
22603  * Copyright(c) 2006-2007, Ext JS, LLC.
22604  *
22605  * Originally Released Under LGPL - original licence link has changed is not relivant.
22606  *
22607  * Fork - LGPL
22608  * <script type="text/javascript">
22609  */
22610  
22611
22612 /**
22613  * @class Roo.form.ComboBox
22614  * @extends Roo.form.TriggerField
22615  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22616  * @constructor
22617  * Create a new ComboBox.
22618  * @param {Object} config Configuration options
22619  */
22620 Roo.form.ComboBox = function(config){
22621     Roo.form.ComboBox.superclass.constructor.call(this, config);
22622     this.addEvents({
22623         /**
22624          * @event expand
22625          * Fires when the dropdown list is expanded
22626              * @param {Roo.form.ComboBox} combo This combo box
22627              */
22628         'expand' : true,
22629         /**
22630          * @event collapse
22631          * Fires when the dropdown list is collapsed
22632              * @param {Roo.form.ComboBox} combo This combo box
22633              */
22634         'collapse' : true,
22635         /**
22636          * @event beforeselect
22637          * Fires before a list item is selected. Return false to cancel the selection.
22638              * @param {Roo.form.ComboBox} combo This combo box
22639              * @param {Roo.data.Record} record The data record returned from the underlying store
22640              * @param {Number} index The index of the selected item in the dropdown list
22641              */
22642         'beforeselect' : true,
22643         /**
22644          * @event select
22645          * Fires when a list item is selected
22646              * @param {Roo.form.ComboBox} combo This combo box
22647              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22648              * @param {Number} index The index of the selected item in the dropdown list
22649              */
22650         'select' : true,
22651         /**
22652          * @event beforequery
22653          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22654          * The event object passed has these properties:
22655              * @param {Roo.form.ComboBox} combo This combo box
22656              * @param {String} query The query
22657              * @param {Boolean} forceAll true to force "all" query
22658              * @param {Boolean} cancel true to cancel the query
22659              * @param {Object} e The query event object
22660              */
22661         'beforequery': true,
22662          /**
22663          * @event add
22664          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22665              * @param {Roo.form.ComboBox} combo This combo box
22666              */
22667         'add' : true,
22668         /**
22669          * @event edit
22670          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22671              * @param {Roo.form.ComboBox} combo This combo box
22672              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22673              */
22674         'edit' : true
22675         
22676         
22677     });
22678     if(this.transform){
22679         this.allowDomMove = false;
22680         var s = Roo.getDom(this.transform);
22681         if(!this.hiddenName){
22682             this.hiddenName = s.name;
22683         }
22684         if(!this.store){
22685             this.mode = 'local';
22686             var d = [], opts = s.options;
22687             for(var i = 0, len = opts.length;i < len; i++){
22688                 var o = opts[i];
22689                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22690                 if(o.selected) {
22691                     this.value = value;
22692                 }
22693                 d.push([value, o.text]);
22694             }
22695             this.store = new Roo.data.SimpleStore({
22696                 'id': 0,
22697                 fields: ['value', 'text'],
22698                 data : d
22699             });
22700             this.valueField = 'value';
22701             this.displayField = 'text';
22702         }
22703         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22704         if(!this.lazyRender){
22705             this.target = true;
22706             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22707             s.parentNode.removeChild(s); // remove it
22708             this.render(this.el.parentNode);
22709         }else{
22710             s.parentNode.removeChild(s); // remove it
22711         }
22712
22713     }
22714     if (this.store) {
22715         this.store = Roo.factory(this.store, Roo.data);
22716     }
22717     
22718     this.selectedIndex = -1;
22719     if(this.mode == 'local'){
22720         if(config.queryDelay === undefined){
22721             this.queryDelay = 10;
22722         }
22723         if(config.minChars === undefined){
22724             this.minChars = 0;
22725         }
22726     }
22727 };
22728
22729 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22730     /**
22731      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22732      */
22733     /**
22734      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22735      * rendering into an Roo.Editor, defaults to false)
22736      */
22737     /**
22738      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22739      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22740      */
22741     /**
22742      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22743      */
22744     /**
22745      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22746      * the dropdown list (defaults to undefined, with no header element)
22747      */
22748
22749      /**
22750      * @cfg {String/Roo.Template} tpl The template to use to render the output
22751      */
22752      
22753     // private
22754     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22755     /**
22756      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22757      */
22758     listWidth: undefined,
22759     /**
22760      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22761      * mode = 'remote' or 'text' if mode = 'local')
22762      */
22763     displayField: undefined,
22764     /**
22765      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22766      * mode = 'remote' or 'value' if mode = 'local'). 
22767      * Note: use of a valueField requires the user make a selection
22768      * in order for a value to be mapped.
22769      */
22770     valueField: undefined,
22771     /**
22772      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22773      * field's data value (defaults to the underlying DOM element's name)
22774      */
22775     hiddenName: undefined,
22776     /**
22777      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22778      */
22779     listClass: '',
22780     /**
22781      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22782      */
22783     selectedClass: 'x-combo-selected',
22784     /**
22785      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22786      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22787      * which displays a downward arrow icon).
22788      */
22789     triggerClass : 'x-form-arrow-trigger',
22790     /**
22791      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22792      */
22793     shadow:'sides',
22794     /**
22795      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22796      * anchor positions (defaults to 'tl-bl')
22797      */
22798     listAlign: 'tl-bl?',
22799     /**
22800      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22801      */
22802     maxHeight: 300,
22803     /**
22804      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22805      * query specified by the allQuery config option (defaults to 'query')
22806      */
22807     triggerAction: 'query',
22808     /**
22809      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22810      * (defaults to 4, does not apply if editable = false)
22811      */
22812     minChars : 4,
22813     /**
22814      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22815      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22816      */
22817     typeAhead: false,
22818     /**
22819      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22820      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22821      */
22822     queryDelay: 500,
22823     /**
22824      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22825      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22826      */
22827     pageSize: 0,
22828     /**
22829      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22830      * when editable = true (defaults to false)
22831      */
22832     selectOnFocus:false,
22833     /**
22834      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22835      */
22836     queryParam: 'query',
22837     /**
22838      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22839      * when mode = 'remote' (defaults to 'Loading...')
22840      */
22841     loadingText: 'Loading...',
22842     /**
22843      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22844      */
22845     resizable: false,
22846     /**
22847      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22848      */
22849     handleHeight : 8,
22850     /**
22851      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22852      * traditional select (defaults to true)
22853      */
22854     editable: true,
22855     /**
22856      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22857      */
22858     allQuery: '',
22859     /**
22860      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22861      */
22862     mode: 'remote',
22863     /**
22864      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22865      * listWidth has a higher value)
22866      */
22867     minListWidth : 70,
22868     /**
22869      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22870      * allow the user to set arbitrary text into the field (defaults to false)
22871      */
22872     forceSelection:false,
22873     /**
22874      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22875      * if typeAhead = true (defaults to 250)
22876      */
22877     typeAheadDelay : 250,
22878     /**
22879      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22880      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22881      */
22882     valueNotFoundText : undefined,
22883     /**
22884      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22885      */
22886     blockFocus : false,
22887     
22888     /**
22889      * @cfg {Boolean} disableClear Disable showing of clear button.
22890      */
22891     disableClear : false,
22892     /**
22893      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22894      */
22895     alwaysQuery : false,
22896     
22897     //private
22898     addicon : false,
22899     editicon: false,
22900     
22901     
22902     // private
22903     onRender : function(ct, position){
22904         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22905         if(this.hiddenName){
22906             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22907                     'before', true);
22908             this.hiddenField.value =
22909                 this.hiddenValue !== undefined ? this.hiddenValue :
22910                 this.value !== undefined ? this.value : '';
22911
22912             // prevent input submission
22913             this.el.dom.removeAttribute('name');
22914         }
22915         if(Roo.isGecko){
22916             this.el.dom.setAttribute('autocomplete', 'off');
22917         }
22918
22919         var cls = 'x-combo-list';
22920
22921         this.list = new Roo.Layer({
22922             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22923         });
22924
22925         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22926         this.list.setWidth(lw);
22927         this.list.swallowEvent('mousewheel');
22928         this.assetHeight = 0;
22929
22930         if(this.title){
22931             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22932             this.assetHeight += this.header.getHeight();
22933         }
22934
22935         this.innerList = this.list.createChild({cls:cls+'-inner'});
22936         this.innerList.on('mouseover', this.onViewOver, this);
22937         this.innerList.on('mousemove', this.onViewMove, this);
22938         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22939         
22940         if(this.allowBlank && !this.pageSize && !this.disableClear){
22941             this.footer = this.list.createChild({cls:cls+'-ft'});
22942             this.pageTb = new Roo.Toolbar(this.footer);
22943            
22944         }
22945         if(this.pageSize){
22946             this.footer = this.list.createChild({cls:cls+'-ft'});
22947             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22948                     {pageSize: this.pageSize});
22949             
22950         }
22951         
22952         if (this.pageTb && this.allowBlank && !this.disableClear) {
22953             var _this = this;
22954             this.pageTb.add(new Roo.Toolbar.Fill(), {
22955                 cls: 'x-btn-icon x-btn-clear',
22956                 text: '&#160;',
22957                 handler: function()
22958                 {
22959                     _this.collapse();
22960                     _this.clearValue();
22961                     _this.onSelect(false, -1);
22962                 }
22963             });
22964         }
22965         if (this.footer) {
22966             this.assetHeight += this.footer.getHeight();
22967         }
22968         
22969
22970         if(!this.tpl){
22971             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22972         }
22973
22974         this.view = new Roo.View(this.innerList, this.tpl, {
22975             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22976         });
22977
22978         this.view.on('click', this.onViewClick, this);
22979
22980         this.store.on('beforeload', this.onBeforeLoad, this);
22981         this.store.on('load', this.onLoad, this);
22982         this.store.on('loadexception', this.collapse, this);
22983
22984         if(this.resizable){
22985             this.resizer = new Roo.Resizable(this.list,  {
22986                pinned:true, handles:'se'
22987             });
22988             this.resizer.on('resize', function(r, w, h){
22989                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22990                 this.listWidth = w;
22991                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22992                 this.restrictHeight();
22993             }, this);
22994             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22995         }
22996         if(!this.editable){
22997             this.editable = true;
22998             this.setEditable(false);
22999         }  
23000         
23001         
23002         if (typeof(this.events.add.listeners) != 'undefined') {
23003             
23004             this.addicon = this.wrap.createChild(
23005                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23006        
23007             this.addicon.on('click', function(e) {
23008                 this.fireEvent('add', this);
23009             }, this);
23010         }
23011         if (typeof(this.events.edit.listeners) != 'undefined') {
23012             
23013             this.editicon = this.wrap.createChild(
23014                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23015             if (this.addicon) {
23016                 this.editicon.setStyle('margin-left', '40px');
23017             }
23018             this.editicon.on('click', function(e) {
23019                 
23020                 // we fire even  if inothing is selected..
23021                 this.fireEvent('edit', this, this.lastData );
23022                 
23023             }, this);
23024         }
23025         
23026         
23027         
23028     },
23029
23030     // private
23031     initEvents : function(){
23032         Roo.form.ComboBox.superclass.initEvents.call(this);
23033
23034         this.keyNav = new Roo.KeyNav(this.el, {
23035             "up" : function(e){
23036                 this.inKeyMode = true;
23037                 this.selectPrev();
23038             },
23039
23040             "down" : function(e){
23041                 if(!this.isExpanded()){
23042                     this.onTriggerClick();
23043                 }else{
23044                     this.inKeyMode = true;
23045                     this.selectNext();
23046                 }
23047             },
23048
23049             "enter" : function(e){
23050                 this.onViewClick();
23051                 //return true;
23052             },
23053
23054             "esc" : function(e){
23055                 this.collapse();
23056             },
23057
23058             "tab" : function(e){
23059                 this.onViewClick(false);
23060                 return true;
23061             },
23062
23063             scope : this,
23064
23065             doRelay : function(foo, bar, hname){
23066                 if(hname == 'down' || this.scope.isExpanded()){
23067                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23068                 }
23069                 return true;
23070             },
23071
23072             forceKeyDown: true
23073         });
23074         this.queryDelay = Math.max(this.queryDelay || 10,
23075                 this.mode == 'local' ? 10 : 250);
23076         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23077         if(this.typeAhead){
23078             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23079         }
23080         if(this.editable !== false){
23081             this.el.on("keyup", this.onKeyUp, this);
23082         }
23083         if(this.forceSelection){
23084             this.on('blur', this.doForce, this);
23085         }
23086     },
23087
23088     onDestroy : function(){
23089         if(this.view){
23090             this.view.setStore(null);
23091             this.view.el.removeAllListeners();
23092             this.view.el.remove();
23093             this.view.purgeListeners();
23094         }
23095         if(this.list){
23096             this.list.destroy();
23097         }
23098         if(this.store){
23099             this.store.un('beforeload', this.onBeforeLoad, this);
23100             this.store.un('load', this.onLoad, this);
23101             this.store.un('loadexception', this.collapse, this);
23102         }
23103         Roo.form.ComboBox.superclass.onDestroy.call(this);
23104     },
23105
23106     // private
23107     fireKey : function(e){
23108         if(e.isNavKeyPress() && !this.list.isVisible()){
23109             this.fireEvent("specialkey", this, e);
23110         }
23111     },
23112
23113     // private
23114     onResize: function(w, h){
23115         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23116         
23117         if(typeof w != 'number'){
23118             // we do not handle it!?!?
23119             return;
23120         }
23121         var tw = this.trigger.getWidth();
23122         tw += this.addicon ? this.addicon.getWidth() : 0;
23123         tw += this.editicon ? this.editicon.getWidth() : 0;
23124         var x = w - tw;
23125         this.el.setWidth( this.adjustWidth('input', x));
23126             
23127         this.trigger.setStyle('left', x+'px');
23128         
23129         if(this.list && this.listWidth === undefined){
23130             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23131             this.list.setWidth(lw);
23132             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23133         }
23134         
23135     
23136         
23137     },
23138
23139     /**
23140      * Allow or prevent the user from directly editing the field text.  If false is passed,
23141      * the user will only be able to select from the items defined in the dropdown list.  This method
23142      * is the runtime equivalent of setting the 'editable' config option at config time.
23143      * @param {Boolean} value True to allow the user to directly edit the field text
23144      */
23145     setEditable : function(value){
23146         if(value == this.editable){
23147             return;
23148         }
23149         this.editable = value;
23150         if(!value){
23151             this.el.dom.setAttribute('readOnly', true);
23152             this.el.on('mousedown', this.onTriggerClick,  this);
23153             this.el.addClass('x-combo-noedit');
23154         }else{
23155             this.el.dom.setAttribute('readOnly', false);
23156             this.el.un('mousedown', this.onTriggerClick,  this);
23157             this.el.removeClass('x-combo-noedit');
23158         }
23159     },
23160
23161     // private
23162     onBeforeLoad : function(){
23163         if(!this.hasFocus){
23164             return;
23165         }
23166         this.innerList.update(this.loadingText ?
23167                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23168         this.restrictHeight();
23169         this.selectedIndex = -1;
23170     },
23171
23172     // private
23173     onLoad : function(){
23174         if(!this.hasFocus){
23175             return;
23176         }
23177         if(this.store.getCount() > 0){
23178             this.expand();
23179             this.restrictHeight();
23180             if(this.lastQuery == this.allQuery){
23181                 if(this.editable){
23182                     this.el.dom.select();
23183                 }
23184                 if(!this.selectByValue(this.value, true)){
23185                     this.select(0, true);
23186                 }
23187             }else{
23188                 this.selectNext();
23189                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23190                     this.taTask.delay(this.typeAheadDelay);
23191                 }
23192             }
23193         }else{
23194             this.onEmptyResults();
23195         }
23196         //this.el.focus();
23197     },
23198
23199     // private
23200     onTypeAhead : function(){
23201         if(this.store.getCount() > 0){
23202             var r = this.store.getAt(0);
23203             var newValue = r.data[this.displayField];
23204             var len = newValue.length;
23205             var selStart = this.getRawValue().length;
23206             if(selStart != len){
23207                 this.setRawValue(newValue);
23208                 this.selectText(selStart, newValue.length);
23209             }
23210         }
23211     },
23212
23213     // private
23214     onSelect : function(record, index){
23215         if(this.fireEvent('beforeselect', this, record, index) !== false){
23216             this.setFromData(index > -1 ? record.data : false);
23217             this.collapse();
23218             this.fireEvent('select', this, record, index);
23219         }
23220     },
23221
23222     /**
23223      * Returns the currently selected field value or empty string if no value is set.
23224      * @return {String} value The selected value
23225      */
23226     getValue : function(){
23227         if(this.valueField){
23228             return typeof this.value != 'undefined' ? this.value : '';
23229         }else{
23230             return Roo.form.ComboBox.superclass.getValue.call(this);
23231         }
23232     },
23233
23234     /**
23235      * Clears any text/value currently set in the field
23236      */
23237     clearValue : function(){
23238         if(this.hiddenField){
23239             this.hiddenField.value = '';
23240         }
23241         this.value = '';
23242         this.setRawValue('');
23243         this.lastSelectionText = '';
23244         this.applyEmptyText();
23245     },
23246
23247     /**
23248      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23249      * will be displayed in the field.  If the value does not match the data value of an existing item,
23250      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23251      * Otherwise the field will be blank (although the value will still be set).
23252      * @param {String} value The value to match
23253      */
23254     setValue : function(v){
23255         var text = v;
23256         if(this.valueField){
23257             var r = this.findRecord(this.valueField, v);
23258             if(r){
23259                 text = r.data[this.displayField];
23260             }else if(this.valueNotFoundText !== undefined){
23261                 text = this.valueNotFoundText;
23262             }
23263         }
23264         this.lastSelectionText = text;
23265         if(this.hiddenField){
23266             this.hiddenField.value = v;
23267         }
23268         Roo.form.ComboBox.superclass.setValue.call(this, text);
23269         this.value = v;
23270     },
23271     /**
23272      * @property {Object} the last set data for the element
23273      */
23274     
23275     lastData : false,
23276     /**
23277      * Sets the value of the field based on a object which is related to the record format for the store.
23278      * @param {Object} value the value to set as. or false on reset?
23279      */
23280     setFromData : function(o){
23281         var dv = ''; // display value
23282         var vv = ''; // value value..
23283         this.lastData = o;
23284         if (this.displayField) {
23285             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23286         } else {
23287             // this is an error condition!!!
23288             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23289         }
23290         
23291         if(this.valueField){
23292             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23293         }
23294         if(this.hiddenField){
23295             this.hiddenField.value = vv;
23296             
23297             this.lastSelectionText = dv;
23298             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23299             this.value = vv;
23300             return;
23301         }
23302         // no hidden field.. - we store the value in 'value', but still display
23303         // display field!!!!
23304         this.lastSelectionText = dv;
23305         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23306         this.value = vv;
23307         
23308         
23309     },
23310     // private
23311     reset : function(){
23312         // overridden so that last data is reset..
23313         this.setValue(this.originalValue);
23314         this.clearInvalid();
23315         this.lastData = false;
23316     },
23317     // private
23318     findRecord : function(prop, value){
23319         var record;
23320         if(this.store.getCount() > 0){
23321             this.store.each(function(r){
23322                 if(r.data[prop] == value){
23323                     record = r;
23324                     return false;
23325                 }
23326             });
23327         }
23328         return record;
23329     },
23330
23331     // private
23332     onViewMove : function(e, t){
23333         this.inKeyMode = false;
23334     },
23335
23336     // private
23337     onViewOver : function(e, t){
23338         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23339             return;
23340         }
23341         var item = this.view.findItemFromChild(t);
23342         if(item){
23343             var index = this.view.indexOf(item);
23344             this.select(index, false);
23345         }
23346     },
23347
23348     // private
23349     onViewClick : function(doFocus){
23350         var index = this.view.getSelectedIndexes()[0];
23351         var r = this.store.getAt(index);
23352         if(r){
23353             this.onSelect(r, index);
23354         }
23355         if(doFocus !== false && !this.blockFocus){
23356             this.el.focus();
23357         }
23358     },
23359
23360     // private
23361     restrictHeight : function(){
23362         this.innerList.dom.style.height = '';
23363         var inner = this.innerList.dom;
23364         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23365         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23366         this.list.beginUpdate();
23367         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23368         this.list.alignTo(this.el, this.listAlign);
23369         this.list.endUpdate();
23370     },
23371
23372     // private
23373     onEmptyResults : function(){
23374         this.collapse();
23375     },
23376
23377     /**
23378      * Returns true if the dropdown list is expanded, else false.
23379      */
23380     isExpanded : function(){
23381         return this.list.isVisible();
23382     },
23383
23384     /**
23385      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23386      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23387      * @param {String} value The data value of the item to select
23388      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23389      * selected item if it is not currently in view (defaults to true)
23390      * @return {Boolean} True if the value matched an item in the list, else false
23391      */
23392     selectByValue : function(v, scrollIntoView){
23393         if(v !== undefined && v !== null){
23394             var r = this.findRecord(this.valueField || this.displayField, v);
23395             if(r){
23396                 this.select(this.store.indexOf(r), scrollIntoView);
23397                 return true;
23398             }
23399         }
23400         return false;
23401     },
23402
23403     /**
23404      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23405      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23406      * @param {Number} index The zero-based index of the list item to select
23407      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23408      * selected item if it is not currently in view (defaults to true)
23409      */
23410     select : function(index, scrollIntoView){
23411         this.selectedIndex = index;
23412         this.view.select(index);
23413         if(scrollIntoView !== false){
23414             var el = this.view.getNode(index);
23415             if(el){
23416                 this.innerList.scrollChildIntoView(el, false);
23417             }
23418         }
23419     },
23420
23421     // private
23422     selectNext : function(){
23423         var ct = this.store.getCount();
23424         if(ct > 0){
23425             if(this.selectedIndex == -1){
23426                 this.select(0);
23427             }else if(this.selectedIndex < ct-1){
23428                 this.select(this.selectedIndex+1);
23429             }
23430         }
23431     },
23432
23433     // private
23434     selectPrev : function(){
23435         var ct = this.store.getCount();
23436         if(ct > 0){
23437             if(this.selectedIndex == -1){
23438                 this.select(0);
23439             }else if(this.selectedIndex != 0){
23440                 this.select(this.selectedIndex-1);
23441             }
23442         }
23443     },
23444
23445     // private
23446     onKeyUp : function(e){
23447         if(this.editable !== false && !e.isSpecialKey()){
23448             this.lastKey = e.getKey();
23449             this.dqTask.delay(this.queryDelay);
23450         }
23451     },
23452
23453     // private
23454     validateBlur : function(){
23455         return !this.list || !this.list.isVisible();   
23456     },
23457
23458     // private
23459     initQuery : function(){
23460         this.doQuery(this.getRawValue());
23461     },
23462
23463     // private
23464     doForce : function(){
23465         if(this.el.dom.value.length > 0){
23466             this.el.dom.value =
23467                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23468             this.applyEmptyText();
23469         }
23470     },
23471
23472     /**
23473      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23474      * query allowing the query action to be canceled if needed.
23475      * @param {String} query The SQL query to execute
23476      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23477      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23478      * saved in the current store (defaults to false)
23479      */
23480     doQuery : function(q, forceAll){
23481         if(q === undefined || q === null){
23482             q = '';
23483         }
23484         var qe = {
23485             query: q,
23486             forceAll: forceAll,
23487             combo: this,
23488             cancel:false
23489         };
23490         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23491             return false;
23492         }
23493         q = qe.query;
23494         forceAll = qe.forceAll;
23495         if(forceAll === true || (q.length >= this.minChars)){
23496             if(this.lastQuery != q || this.alwaysQuery){
23497                 this.lastQuery = q;
23498                 if(this.mode == 'local'){
23499                     this.selectedIndex = -1;
23500                     if(forceAll){
23501                         this.store.clearFilter();
23502                     }else{
23503                         this.store.filter(this.displayField, q);
23504                     }
23505                     this.onLoad();
23506                 }else{
23507                     this.store.baseParams[this.queryParam] = q;
23508                     this.store.load({
23509                         params: this.getParams(q)
23510                     });
23511                     this.expand();
23512                 }
23513             }else{
23514                 this.selectedIndex = -1;
23515                 this.onLoad();   
23516             }
23517         }
23518     },
23519
23520     // private
23521     getParams : function(q){
23522         var p = {};
23523         //p[this.queryParam] = q;
23524         if(this.pageSize){
23525             p.start = 0;
23526             p.limit = this.pageSize;
23527         }
23528         return p;
23529     },
23530
23531     /**
23532      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23533      */
23534     collapse : function(){
23535         if(!this.isExpanded()){
23536             return;
23537         }
23538         this.list.hide();
23539         Roo.get(document).un('mousedown', this.collapseIf, this);
23540         Roo.get(document).un('mousewheel', this.collapseIf, this);
23541         if (!this.editable) {
23542             Roo.get(document).un('keydown', this.listKeyPress, this);
23543         }
23544         this.fireEvent('collapse', this);
23545     },
23546
23547     // private
23548     collapseIf : function(e){
23549         if(!e.within(this.wrap) && !e.within(this.list)){
23550             this.collapse();
23551         }
23552     },
23553
23554     /**
23555      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23556      */
23557     expand : function(){
23558         if(this.isExpanded() || !this.hasFocus){
23559             return;
23560         }
23561         this.list.alignTo(this.el, this.listAlign);
23562         this.list.show();
23563         Roo.get(document).on('mousedown', this.collapseIf, this);
23564         Roo.get(document).on('mousewheel', this.collapseIf, this);
23565         if (!this.editable) {
23566             Roo.get(document).on('keydown', this.listKeyPress, this);
23567         }
23568         
23569         this.fireEvent('expand', this);
23570     },
23571
23572     // private
23573     // Implements the default empty TriggerField.onTriggerClick function
23574     onTriggerClick : function(){
23575         if(this.disabled){
23576             return;
23577         }
23578         if(this.isExpanded()){
23579             this.collapse();
23580             if (!this.blockFocus) {
23581                 this.el.focus();
23582             }
23583             
23584         }else {
23585             this.hasFocus = true;
23586             if(this.triggerAction == 'all') {
23587                 this.doQuery(this.allQuery, true);
23588             } else {
23589                 this.doQuery(this.getRawValue());
23590             }
23591             if (!this.blockFocus) {
23592                 this.el.focus();
23593             }
23594         }
23595     },
23596     listKeyPress : function(e)
23597     {
23598         //Roo.log('listkeypress');
23599         // scroll to first matching element based on key pres..
23600         if (e.isSpecialKey()) {
23601             return false;
23602         }
23603         var k = String.fromCharCode(e.getKey()).toUpperCase();
23604         //Roo.log(k);
23605         var match  = false;
23606         var csel = this.view.getSelectedNodes();
23607         var cselitem = false;
23608         if (csel.length) {
23609             var ix = this.view.indexOf(csel[0]);
23610             cselitem  = this.store.getAt(ix);
23611             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23612                 cselitem = false;
23613             }
23614             
23615         }
23616         
23617         this.store.each(function(v) { 
23618             if (cselitem) {
23619                 // start at existing selection.
23620                 if (cselitem.id == v.id) {
23621                     cselitem = false;
23622                 }
23623                 return;
23624             }
23625                 
23626             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23627                 match = this.store.indexOf(v);
23628                 return false;
23629             }
23630         }, this);
23631         
23632         if (match === false) {
23633             return true; // no more action?
23634         }
23635         // scroll to?
23636         this.view.select(match);
23637         var sn = Roo.get(this.view.getSelectedNodes()[0])
23638         sn.scrollIntoView(sn.dom.parentNode, false);
23639     }
23640
23641     /** 
23642     * @cfg {Boolean} grow 
23643     * @hide 
23644     */
23645     /** 
23646     * @cfg {Number} growMin 
23647     * @hide 
23648     */
23649     /** 
23650     * @cfg {Number} growMax 
23651     * @hide 
23652     */
23653     /**
23654      * @hide
23655      * @method autoSize
23656      */
23657 });/*
23658  * Based on:
23659  * Ext JS Library 1.1.1
23660  * Copyright(c) 2006-2007, Ext JS, LLC.
23661  *
23662  * Originally Released Under LGPL - original licence link has changed is not relivant.
23663  *
23664  * Fork - LGPL
23665  * <script type="text/javascript">
23666  */
23667 /**
23668  * @class Roo.form.Checkbox
23669  * @extends Roo.form.Field
23670  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23671  * @constructor
23672  * Creates a new Checkbox
23673  * @param {Object} config Configuration options
23674  */
23675 Roo.form.Checkbox = function(config){
23676     Roo.form.Checkbox.superclass.constructor.call(this, config);
23677     this.addEvents({
23678         /**
23679          * @event check
23680          * Fires when the checkbox is checked or unchecked.
23681              * @param {Roo.form.Checkbox} this This checkbox
23682              * @param {Boolean} checked The new checked value
23683              */
23684         check : true
23685     });
23686 };
23687
23688 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23689     /**
23690      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23691      */
23692     focusClass : undefined,
23693     /**
23694      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23695      */
23696     fieldClass: "x-form-field",
23697     /**
23698      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23699      */
23700     checked: false,
23701     /**
23702      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23703      * {tag: "input", type: "checkbox", autocomplete: "off"})
23704      */
23705     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23706     /**
23707      * @cfg {String} boxLabel The text that appears beside the checkbox
23708      */
23709     boxLabel : "",
23710     /**
23711      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23712      */  
23713     inputValue : '1',
23714     /**
23715      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23716      */
23717      valueOff: '0', // value when not checked..
23718
23719     actionMode : 'viewEl', 
23720     //
23721     // private
23722     itemCls : 'x-menu-check-item x-form-item',
23723     groupClass : 'x-menu-group-item',
23724     inputType : 'hidden',
23725     
23726     
23727     inSetChecked: false, // check that we are not calling self...
23728     
23729     inputElement: false, // real input element?
23730     basedOn: false, // ????
23731     
23732     isFormField: true, // not sure where this is needed!!!!
23733
23734     onResize : function(){
23735         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23736         if(!this.boxLabel){
23737             this.el.alignTo(this.wrap, 'c-c');
23738         }
23739     },
23740
23741     initEvents : function(){
23742         Roo.form.Checkbox.superclass.initEvents.call(this);
23743         this.el.on("click", this.onClick,  this);
23744         this.el.on("change", this.onClick,  this);
23745     },
23746
23747
23748     getResizeEl : function(){
23749         return this.wrap;
23750     },
23751
23752     getPositionEl : function(){
23753         return this.wrap;
23754     },
23755
23756     // private
23757     onRender : function(ct, position){
23758         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23759         /*
23760         if(this.inputValue !== undefined){
23761             this.el.dom.value = this.inputValue;
23762         }
23763         */
23764         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23765         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23766         var viewEl = this.wrap.createChild({ 
23767             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23768         this.viewEl = viewEl;   
23769         this.wrap.on('click', this.onClick,  this); 
23770         
23771         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23772         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23773         
23774         
23775         
23776         if(this.boxLabel){
23777             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23778         //    viewEl.on('click', this.onClick,  this); 
23779         }
23780         //if(this.checked){
23781             this.setChecked(this.checked);
23782         //}else{
23783             //this.checked = this.el.dom;
23784         //}
23785
23786     },
23787
23788     // private
23789     initValue : Roo.emptyFn,
23790
23791     /**
23792      * Returns the checked state of the checkbox.
23793      * @return {Boolean} True if checked, else false
23794      */
23795     getValue : function(){
23796         if(this.el){
23797             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23798         }
23799         return this.valueOff;
23800         
23801     },
23802
23803         // private
23804     onClick : function(){ 
23805         this.setChecked(!this.checked);
23806
23807         //if(this.el.dom.checked != this.checked){
23808         //    this.setValue(this.el.dom.checked);
23809        // }
23810     },
23811
23812     /**
23813      * Sets the checked state of the checkbox.
23814      * On is always based on a string comparison between inputValue and the param.
23815      * @param {Boolean/String} value - the value to set 
23816      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23817      */
23818     setValue : function(v,suppressEvent){
23819         
23820         
23821         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23822         //if(this.el && this.el.dom){
23823         //    this.el.dom.checked = this.checked;
23824         //    this.el.dom.defaultChecked = this.checked;
23825         //}
23826         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23827         //this.fireEvent("check", this, this.checked);
23828     },
23829     // private..
23830     setChecked : function(state,suppressEvent)
23831     {
23832         if (this.inSetChecked) {
23833             this.checked = state;
23834             return;
23835         }
23836         
23837     
23838         if(this.wrap){
23839             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23840         }
23841         this.checked = state;
23842         if(suppressEvent !== true){
23843             this.fireEvent('check', this, state);
23844         }
23845         this.inSetChecked = true;
23846         this.el.dom.value = state ? this.inputValue : this.valueOff;
23847         this.inSetChecked = false;
23848         
23849     },
23850     // handle setting of hidden value by some other method!!?!?
23851     setFromHidden: function()
23852     {
23853         if(!this.el){
23854             return;
23855         }
23856         //console.log("SET FROM HIDDEN");
23857         //alert('setFrom hidden');
23858         this.setValue(this.el.dom.value);
23859     },
23860     
23861     onDestroy : function()
23862     {
23863         if(this.viewEl){
23864             Roo.get(this.viewEl).remove();
23865         }
23866          
23867         Roo.form.Checkbox.superclass.onDestroy.call(this);
23868     }
23869
23870 });/*
23871  * Based on:
23872  * Ext JS Library 1.1.1
23873  * Copyright(c) 2006-2007, Ext JS, LLC.
23874  *
23875  * Originally Released Under LGPL - original licence link has changed is not relivant.
23876  *
23877  * Fork - LGPL
23878  * <script type="text/javascript">
23879  */
23880  
23881 /**
23882  * @class Roo.form.Radio
23883  * @extends Roo.form.Checkbox
23884  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23885  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23886  * @constructor
23887  * Creates a new Radio
23888  * @param {Object} config Configuration options
23889  */
23890 Roo.form.Radio = function(){
23891     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23892 };
23893 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23894     inputType: 'radio',
23895
23896     /**
23897      * If this radio is part of a group, it will return the selected value
23898      * @return {String}
23899      */
23900     getGroupValue : function(){
23901         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23902     }
23903 });//<script type="text/javascript">
23904
23905 /*
23906  * Ext JS Library 1.1.1
23907  * Copyright(c) 2006-2007, Ext JS, LLC.
23908  * licensing@extjs.com
23909  * 
23910  * http://www.extjs.com/license
23911  */
23912  
23913  /*
23914   * 
23915   * Known bugs:
23916   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23917   * - IE ? - no idea how much works there.
23918   * 
23919   * 
23920   * 
23921   */
23922  
23923
23924 /**
23925  * @class Ext.form.HtmlEditor
23926  * @extends Ext.form.Field
23927  * Provides a lightweight HTML Editor component.
23928  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23929  * 
23930  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23931  * supported by this editor.</b><br/><br/>
23932  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23933  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23934  */
23935 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23936       /**
23937      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23938      */
23939     toolbars : false,
23940     /**
23941      * @cfg {String} createLinkText The default text for the create link prompt
23942      */
23943     createLinkText : 'Please enter the URL for the link:',
23944     /**
23945      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23946      */
23947     defaultLinkValue : 'http:/'+'/',
23948    
23949     
23950     // id of frame..
23951     frameId: false,
23952     
23953     // private properties
23954     validationEvent : false,
23955     deferHeight: true,
23956     initialized : false,
23957     activated : false,
23958     sourceEditMode : false,
23959     onFocus : Roo.emptyFn,
23960     iframePad:3,
23961     hideMode:'offsets',
23962     defaultAutoCreate : {
23963         tag: "textarea",
23964         style:"width:500px;height:300px;",
23965         autocomplete: "off"
23966     },
23967
23968     // private
23969     initComponent : function(){
23970         this.addEvents({
23971             /**
23972              * @event initialize
23973              * Fires when the editor is fully initialized (including the iframe)
23974              * @param {HtmlEditor} this
23975              */
23976             initialize: true,
23977             /**
23978              * @event activate
23979              * Fires when the editor is first receives the focus. Any insertion must wait
23980              * until after this event.
23981              * @param {HtmlEditor} this
23982              */
23983             activate: true,
23984              /**
23985              * @event beforesync
23986              * Fires before the textarea is updated with content from the editor iframe. Return false
23987              * to cancel the sync.
23988              * @param {HtmlEditor} this
23989              * @param {String} html
23990              */
23991             beforesync: true,
23992              /**
23993              * @event beforepush
23994              * Fires before the iframe editor is updated with content from the textarea. Return false
23995              * to cancel the push.
23996              * @param {HtmlEditor} this
23997              * @param {String} html
23998              */
23999             beforepush: true,
24000              /**
24001              * @event sync
24002              * Fires when the textarea is updated with content from the editor iframe.
24003              * @param {HtmlEditor} this
24004              * @param {String} html
24005              */
24006             sync: true,
24007              /**
24008              * @event push
24009              * Fires when the iframe editor is updated with content from the textarea.
24010              * @param {HtmlEditor} this
24011              * @param {String} html
24012              */
24013             push: true,
24014              /**
24015              * @event editmodechange
24016              * Fires when the editor switches edit modes
24017              * @param {HtmlEditor} this
24018              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24019              */
24020             editmodechange: true,
24021             /**
24022              * @event editorevent
24023              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24024              * @param {HtmlEditor} this
24025              */
24026             editorevent: true
24027         })
24028     },
24029
24030     /**
24031      * Protected method that will not generally be called directly. It
24032      * is called when the editor creates its toolbar. Override this method if you need to
24033      * add custom toolbar buttons.
24034      * @param {HtmlEditor} editor
24035      */
24036     createToolbar : function(editor){
24037         if (!editor.toolbars || !editor.toolbars.length) {
24038             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24039         }
24040         
24041         for (var i =0 ; i < editor.toolbars.length;i++) {
24042             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24043             editor.toolbars[i].init(editor);
24044         }
24045          
24046         
24047     },
24048
24049     /**
24050      * Protected method that will not generally be called directly. It
24051      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24052      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24053      */
24054     getDocMarkup : function(){
24055         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24056     },
24057
24058     // private
24059     onRender : function(ct, position){
24060         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24061         this.el.dom.style.border = '0 none';
24062         this.el.dom.setAttribute('tabIndex', -1);
24063         this.el.addClass('x-hidden');
24064         if(Roo.isIE){ // fix IE 1px bogus margin
24065             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24066         }
24067         this.wrap = this.el.wrap({
24068             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24069         });
24070
24071         this.frameId = Roo.id();
24072         this.createToolbar(this);
24073         
24074         
24075         
24076         
24077       
24078         
24079         var iframe = this.wrap.createChild({
24080             tag: 'iframe',
24081             id: this.frameId,
24082             name: this.frameId,
24083             frameBorder : 'no',
24084             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24085         });
24086         
24087        // console.log(iframe);
24088         //this.wrap.dom.appendChild(iframe);
24089
24090         this.iframe = iframe.dom;
24091
24092          this.assignDocWin();
24093         
24094         this.doc.designMode = 'on';
24095        
24096         this.doc.open();
24097         this.doc.write(this.getDocMarkup());
24098         this.doc.close();
24099
24100         
24101         var task = { // must defer to wait for browser to be ready
24102             run : function(){
24103                 //console.log("run task?" + this.doc.readyState);
24104                 this.assignDocWin();
24105                 if(this.doc.body || this.doc.readyState == 'complete'){
24106                     try {
24107                         this.doc.designMode="on";
24108                     } catch (e) {
24109                         return;
24110                     }
24111                     Roo.TaskMgr.stop(task);
24112                     this.initEditor.defer(10, this);
24113                 }
24114             },
24115             interval : 10,
24116             duration:10000,
24117             scope: this
24118         };
24119         Roo.TaskMgr.start(task);
24120
24121         if(!this.width){
24122             this.setSize(this.el.getSize());
24123         }
24124     },
24125
24126     // private
24127     onResize : function(w, h){
24128         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24129         if(this.el && this.iframe){
24130             if(typeof w == 'number'){
24131                 var aw = w - this.wrap.getFrameWidth('lr');
24132                 this.el.setWidth(this.adjustWidth('textarea', aw));
24133                 this.iframe.style.width = aw + 'px';
24134             }
24135             if(typeof h == 'number'){
24136                 var tbh = 0;
24137                 for (var i =0; i < this.toolbars.length;i++) {
24138                     // fixme - ask toolbars for heights?
24139                     tbh += this.toolbars[i].tb.el.getHeight();
24140                 }
24141                 
24142                 
24143                 
24144                 
24145                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24146                 this.el.setHeight(this.adjustWidth('textarea', ah));
24147                 this.iframe.style.height = ah + 'px';
24148                 if(this.doc){
24149                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24150                 }
24151             }
24152         }
24153     },
24154
24155     /**
24156      * Toggles the editor between standard and source edit mode.
24157      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24158      */
24159     toggleSourceEdit : function(sourceEditMode){
24160         
24161         this.sourceEditMode = sourceEditMode === true;
24162         
24163         if(this.sourceEditMode){
24164           
24165             this.syncValue();
24166             this.iframe.className = 'x-hidden';
24167             this.el.removeClass('x-hidden');
24168             this.el.dom.removeAttribute('tabIndex');
24169             this.el.focus();
24170         }else{
24171              
24172             this.pushValue();
24173             this.iframe.className = '';
24174             this.el.addClass('x-hidden');
24175             this.el.dom.setAttribute('tabIndex', -1);
24176             this.deferFocus();
24177         }
24178         this.setSize(this.wrap.getSize());
24179         this.fireEvent('editmodechange', this, this.sourceEditMode);
24180     },
24181
24182     // private used internally
24183     createLink : function(){
24184         var url = prompt(this.createLinkText, this.defaultLinkValue);
24185         if(url && url != 'http:/'+'/'){
24186             this.relayCmd('createlink', url);
24187         }
24188     },
24189
24190     // private (for BoxComponent)
24191     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24192
24193     // private (for BoxComponent)
24194     getResizeEl : function(){
24195         return this.wrap;
24196     },
24197
24198     // private (for BoxComponent)
24199     getPositionEl : function(){
24200         return this.wrap;
24201     },
24202
24203     // private
24204     initEvents : function(){
24205         this.originalValue = this.getValue();
24206     },
24207
24208     /**
24209      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24210      * @method
24211      */
24212     markInvalid : Roo.emptyFn,
24213     /**
24214      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24215      * @method
24216      */
24217     clearInvalid : Roo.emptyFn,
24218
24219     setValue : function(v){
24220         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24221         this.pushValue();
24222     },
24223
24224     /**
24225      * Protected method that will not generally be called directly. If you need/want
24226      * custom HTML cleanup, this is the method you should override.
24227      * @param {String} html The HTML to be cleaned
24228      * return {String} The cleaned HTML
24229      */
24230     cleanHtml : function(html){
24231         html = String(html);
24232         if(html.length > 5){
24233             if(Roo.isSafari){ // strip safari nonsense
24234                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24235             }
24236         }
24237         if(html == '&nbsp;'){
24238             html = '';
24239         }
24240         return html;
24241     },
24242
24243     /**
24244      * Protected method that will not generally be called directly. Syncs the contents
24245      * of the editor iframe with the textarea.
24246      */
24247     syncValue : function(){
24248         if(this.initialized){
24249             var bd = (this.doc.body || this.doc.documentElement);
24250             this.cleanUpPaste();
24251             var html = bd.innerHTML;
24252             if(Roo.isSafari){
24253                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24254                 var m = bs.match(/text-align:(.*?);/i);
24255                 if(m && m[1]){
24256                     html = '<div style="'+m[0]+'">' + html + '</div>';
24257                 }
24258             }
24259             html = this.cleanHtml(html);
24260             if(this.fireEvent('beforesync', this, html) !== false){
24261                 this.el.dom.value = html;
24262                 this.fireEvent('sync', this, html);
24263             }
24264         }
24265     },
24266
24267     /**
24268      * Protected method that will not generally be called directly. Pushes the value of the textarea
24269      * into the iframe editor.
24270      */
24271     pushValue : function(){
24272         if(this.initialized){
24273             var v = this.el.dom.value;
24274             if(v.length < 1){
24275                 v = '&#160;';
24276             }
24277             
24278             if(this.fireEvent('beforepush', this, v) !== false){
24279                 var d = (this.doc.body || this.doc.documentElement);
24280                 d.innerHTML = v;
24281                 this.cleanUpPaste();
24282                 this.el.dom.value = d.innerHTML;
24283                 this.fireEvent('push', this, v);
24284             }
24285         }
24286     },
24287
24288     // private
24289     deferFocus : function(){
24290         this.focus.defer(10, this);
24291     },
24292
24293     // doc'ed in Field
24294     focus : function(){
24295         if(this.win && !this.sourceEditMode){
24296             this.win.focus();
24297         }else{
24298             this.el.focus();
24299         }
24300     },
24301     
24302     assignDocWin: function()
24303     {
24304         var iframe = this.iframe;
24305         
24306          if(Roo.isIE){
24307             this.doc = iframe.contentWindow.document;
24308             this.win = iframe.contentWindow;
24309         } else {
24310             if (!Roo.get(this.frameId)) {
24311                 return;
24312             }
24313             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24314             this.win = Roo.get(this.frameId).dom.contentWindow;
24315         }
24316     },
24317     
24318     // private
24319     initEditor : function(){
24320         //console.log("INIT EDITOR");
24321         this.assignDocWin();
24322         
24323         
24324         
24325         this.doc.designMode="on";
24326         this.doc.open();
24327         this.doc.write(this.getDocMarkup());
24328         this.doc.close();
24329         
24330         var dbody = (this.doc.body || this.doc.documentElement);
24331         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24332         // this copies styles from the containing element into thsi one..
24333         // not sure why we need all of this..
24334         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24335         ss['background-attachment'] = 'fixed'; // w3c
24336         dbody.bgProperties = 'fixed'; // ie
24337         Roo.DomHelper.applyStyles(dbody, ss);
24338         Roo.EventManager.on(this.doc, {
24339             'mousedown': this.onEditorEvent,
24340             'dblclick': this.onEditorEvent,
24341             'click': this.onEditorEvent,
24342             'keyup': this.onEditorEvent,
24343             buffer:100,
24344             scope: this
24345         });
24346         if(Roo.isGecko){
24347             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24348         }
24349         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24350             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24351         }
24352         this.initialized = true;
24353
24354         this.fireEvent('initialize', this);
24355         this.pushValue();
24356     },
24357
24358     // private
24359     onDestroy : function(){
24360         
24361         
24362         
24363         if(this.rendered){
24364             
24365             for (var i =0; i < this.toolbars.length;i++) {
24366                 // fixme - ask toolbars for heights?
24367                 this.toolbars[i].onDestroy();
24368             }
24369             
24370             this.wrap.dom.innerHTML = '';
24371             this.wrap.remove();
24372         }
24373     },
24374
24375     // private
24376     onFirstFocus : function(){
24377         
24378         this.assignDocWin();
24379         
24380         
24381         this.activated = true;
24382         for (var i =0; i < this.toolbars.length;i++) {
24383             this.toolbars[i].onFirstFocus();
24384         }
24385        
24386         if(Roo.isGecko){ // prevent silly gecko errors
24387             this.win.focus();
24388             var s = this.win.getSelection();
24389             if(!s.focusNode || s.focusNode.nodeType != 3){
24390                 var r = s.getRangeAt(0);
24391                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24392                 r.collapse(true);
24393                 this.deferFocus();
24394             }
24395             try{
24396                 this.execCmd('useCSS', true);
24397                 this.execCmd('styleWithCSS', false);
24398             }catch(e){}
24399         }
24400         this.fireEvent('activate', this);
24401     },
24402
24403     // private
24404     adjustFont: function(btn){
24405         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24406         //if(Roo.isSafari){ // safari
24407         //    adjust *= 2;
24408        // }
24409         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24410         if(Roo.isSafari){ // safari
24411             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24412             v =  (v < 10) ? 10 : v;
24413             v =  (v > 48) ? 48 : v;
24414             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24415             
24416         }
24417         
24418         
24419         v = Math.max(1, v+adjust);
24420         
24421         this.execCmd('FontSize', v  );
24422     },
24423
24424     onEditorEvent : function(e){
24425         this.fireEvent('editorevent', this, e);
24426       //  this.updateToolbar();
24427         this.syncValue();
24428     },
24429
24430     insertTag : function(tg)
24431     {
24432         // could be a bit smarter... -> wrap the current selected tRoo..
24433         
24434         this.execCmd("formatblock",   tg);
24435         
24436     },
24437     
24438     insertText : function(txt)
24439     {
24440         
24441         
24442         range = this.createRange();
24443         range.deleteContents();
24444                //alert(Sender.getAttribute('label'));
24445                
24446         range.insertNode(this.doc.createTextNode(txt));
24447     } ,
24448     
24449     // private
24450     relayBtnCmd : function(btn){
24451         this.relayCmd(btn.cmd);
24452     },
24453
24454     /**
24455      * Executes a Midas editor command on the editor document and performs necessary focus and
24456      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24457      * @param {String} cmd The Midas command
24458      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24459      */
24460     relayCmd : function(cmd, value){
24461         this.win.focus();
24462         this.execCmd(cmd, value);
24463         this.fireEvent('editorevent', this);
24464         //this.updateToolbar();
24465         this.deferFocus();
24466     },
24467
24468     /**
24469      * Executes a Midas editor command directly on the editor document.
24470      * For visual commands, you should use {@link #relayCmd} instead.
24471      * <b>This should only be called after the editor is initialized.</b>
24472      * @param {String} cmd The Midas command
24473      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24474      */
24475     execCmd : function(cmd, value){
24476         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24477         this.syncValue();
24478     },
24479
24480    
24481     /**
24482      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24483      * to insert tRoo.
24484      * @param {String} text
24485      */
24486     insertAtCursor : function(text){
24487         if(!this.activated){
24488             return;
24489         }
24490         if(Roo.isIE){
24491             this.win.focus();
24492             var r = this.doc.selection.createRange();
24493             if(r){
24494                 r.collapse(true);
24495                 r.pasteHTML(text);
24496                 this.syncValue();
24497                 this.deferFocus();
24498             }
24499         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24500             this.win.focus();
24501             this.execCmd('InsertHTML', text);
24502             this.deferFocus();
24503         }
24504     },
24505  // private
24506     mozKeyPress : function(e){
24507         if(e.ctrlKey){
24508             var c = e.getCharCode(), cmd;
24509           
24510             if(c > 0){
24511                 c = String.fromCharCode(c).toLowerCase();
24512                 switch(c){
24513                     case 'b':
24514                         cmd = 'bold';
24515                     break;
24516                     case 'i':
24517                         cmd = 'italic';
24518                     break;
24519                     case 'u':
24520                         cmd = 'underline';
24521                     case 'v':
24522                         this.cleanUpPaste.defer(100, this);
24523                         return;
24524                     break;
24525                 }
24526                 if(cmd){
24527                     this.win.focus();
24528                     this.execCmd(cmd);
24529                     this.deferFocus();
24530                     e.preventDefault();
24531                 }
24532                 
24533             }
24534         }
24535     },
24536
24537     // private
24538     fixKeys : function(){ // load time branching for fastest keydown performance
24539         if(Roo.isIE){
24540             return function(e){
24541                 var k = e.getKey(), r;
24542                 if(k == e.TAB){
24543                     e.stopEvent();
24544                     r = this.doc.selection.createRange();
24545                     if(r){
24546                         r.collapse(true);
24547                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24548                         this.deferFocus();
24549                     }
24550                     return;
24551                 }
24552                 
24553                 if(k == e.ENTER){
24554                     r = this.doc.selection.createRange();
24555                     if(r){
24556                         var target = r.parentElement();
24557                         if(!target || target.tagName.toLowerCase() != 'li'){
24558                             e.stopEvent();
24559                             r.pasteHTML('<br />');
24560                             r.collapse(false);
24561                             r.select();
24562                         }
24563                     }
24564                 }
24565                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24566                     this.cleanUpPaste.defer(100, this);
24567                     return;
24568                 }
24569                 
24570                 
24571             };
24572         }else if(Roo.isOpera){
24573             return function(e){
24574                 var k = e.getKey();
24575                 if(k == e.TAB){
24576                     e.stopEvent();
24577                     this.win.focus();
24578                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24579                     this.deferFocus();
24580                 }
24581                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24582                     this.cleanUpPaste.defer(100, this);
24583                     return;
24584                 }
24585                 
24586             };
24587         }else if(Roo.isSafari){
24588             return function(e){
24589                 var k = e.getKey();
24590                 
24591                 if(k == e.TAB){
24592                     e.stopEvent();
24593                     this.execCmd('InsertText','\t');
24594                     this.deferFocus();
24595                     return;
24596                 }
24597                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24598                     this.cleanUpPaste.defer(100, this);
24599                     return;
24600                 }
24601                 
24602              };
24603         }
24604     }(),
24605     
24606     getAllAncestors: function()
24607     {
24608         var p = this.getSelectedNode();
24609         var a = [];
24610         if (!p) {
24611             a.push(p); // push blank onto stack..
24612             p = this.getParentElement();
24613         }
24614         
24615         
24616         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24617             a.push(p);
24618             p = p.parentNode;
24619         }
24620         a.push(this.doc.body);
24621         return a;
24622     },
24623     lastSel : false,
24624     lastSelNode : false,
24625     
24626     
24627     getSelection : function() 
24628     {
24629         this.assignDocWin();
24630         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24631     },
24632     
24633     getSelectedNode: function() 
24634     {
24635         // this may only work on Gecko!!!
24636         
24637         // should we cache this!!!!
24638         
24639         
24640         
24641          
24642         var range = this.createRange(this.getSelection());
24643         
24644         if (Roo.isIE) {
24645             var parent = range.parentElement();
24646             while (true) {
24647                 var testRange = range.duplicate();
24648                 testRange.moveToElementText(parent);
24649                 if (testRange.inRange(range)) {
24650                     break;
24651                 }
24652                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24653                     break;
24654                 }
24655                 parent = parent.parentElement;
24656             }
24657             return parent;
24658         }
24659         
24660         
24661         var ar = range.endContainer.childNodes;
24662         if (!ar.length) {
24663             ar = range.commonAncestorContainer.childNodes;
24664             //alert(ar.length);
24665         }
24666         var nodes = [];
24667         var other_nodes = [];
24668         var has_other_nodes = false;
24669         for (var i=0;i<ar.length;i++) {
24670             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24671                 continue;
24672             }
24673             // fullly contained node.
24674             
24675             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24676                 nodes.push(ar[i]);
24677                 continue;
24678             }
24679             
24680             // probably selected..
24681             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24682                 other_nodes.push(ar[i]);
24683                 continue;
24684             }
24685             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24686                 continue;
24687             }
24688             
24689             
24690             has_other_nodes = true;
24691         }
24692         if (!nodes.length && other_nodes.length) {
24693             nodes= other_nodes;
24694         }
24695         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24696             return false;
24697         }
24698         
24699         return nodes[0];
24700     },
24701     createRange: function(sel)
24702     {
24703         // this has strange effects when using with 
24704         // top toolbar - not sure if it's a great idea.
24705         //this.editor.contentWindow.focus();
24706         if (typeof sel != "undefined") {
24707             try {
24708                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24709             } catch(e) {
24710                 return this.doc.createRange();
24711             }
24712         } else {
24713             return this.doc.createRange();
24714         }
24715     },
24716     getParentElement: function()
24717     {
24718         
24719         this.assignDocWin();
24720         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24721         
24722         var range = this.createRange(sel);
24723          
24724         try {
24725             var p = range.commonAncestorContainer;
24726             while (p.nodeType == 3) { // text node
24727                 p = p.parentNode;
24728             }
24729             return p;
24730         } catch (e) {
24731             return null;
24732         }
24733     
24734     },
24735     
24736     
24737     
24738     // BC Hacks - cause I cant work out what i was trying to do..
24739     rangeIntersectsNode : function(range, node)
24740     {
24741         var nodeRange = node.ownerDocument.createRange();
24742         try {
24743             nodeRange.selectNode(node);
24744         }
24745         catch (e) {
24746             nodeRange.selectNodeContents(node);
24747         }
24748
24749         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24750                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24751     },
24752     rangeCompareNode : function(range, node) {
24753         var nodeRange = node.ownerDocument.createRange();
24754         try {
24755             nodeRange.selectNode(node);
24756         } catch (e) {
24757             nodeRange.selectNodeContents(node);
24758         }
24759         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24760         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24761
24762         if (nodeIsBefore && !nodeIsAfter)
24763             return 0;
24764         if (!nodeIsBefore && nodeIsAfter)
24765             return 1;
24766         if (nodeIsBefore && nodeIsAfter)
24767             return 2;
24768
24769         return 3;
24770     },
24771
24772     // private? - in a new class?
24773     cleanUpPaste :  function()
24774     {
24775         // cleans up the whole document..
24776       //  console.log('cleanuppaste');
24777         this.cleanUpChildren(this.doc.body);
24778         
24779         
24780     },
24781     cleanUpChildren : function (n)
24782     {
24783         if (!n.childNodes.length) {
24784             return;
24785         }
24786         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24787            this.cleanUpChild(n.childNodes[i]);
24788         }
24789     },
24790     
24791     
24792         
24793     
24794     cleanUpChild : function (node)
24795     {
24796         //console.log(node);
24797         if (node.nodeName == "#text") {
24798             // clean up silly Windows -- stuff?
24799             return; 
24800         }
24801         if (node.nodeName == "#comment") {
24802             node.parentNode.removeChild(node);
24803             // clean up silly Windows -- stuff?
24804             return; 
24805         }
24806         
24807         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24808             // remove node.
24809             node.parentNode.removeChild(node);
24810             return;
24811             
24812         }
24813         if (!node.attributes || !node.attributes.length) {
24814             this.cleanUpChildren(node);
24815             return;
24816         }
24817         
24818         function cleanAttr(n,v)
24819         {
24820             
24821             if (v.match(/^\./) || v.match(/^\//)) {
24822                 return;
24823             }
24824             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24825                 return;
24826             }
24827             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24828             node.removeAttribute(n);
24829             
24830         }
24831         
24832         function cleanStyle(n,v)
24833         {
24834             if (v.match(/expression/)) { //XSS?? should we even bother..
24835                 node.removeAttribute(n);
24836                 return;
24837             }
24838             
24839             
24840             var parts = v.split(/;/);
24841             Roo.each(parts, function(p) {
24842                 p = p.replace(/\s+/g,'');
24843                 if (!p.length) {
24844                     return;
24845                 }
24846                 var l = p.split(':').shift().replace(/\s+/g,'');
24847                 
24848                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24849                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24850                     node.removeAttribute(n);
24851                     return false;
24852                 }
24853             });
24854             
24855             
24856         }
24857         
24858         
24859         for (var i = node.attributes.length-1; i > -1 ; i--) {
24860             var a = node.attributes[i];
24861             //console.log(a);
24862             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24863                 node.removeAttribute(a.name);
24864                 return;
24865             }
24866             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24867                 cleanAttr(a.name,a.value); // fixme..
24868                 return;
24869             }
24870             if (a.name == 'style') {
24871                 cleanStyle(a.name,a.value);
24872             }
24873             /// clean up MS crap..
24874             if (a.name == 'class') {
24875                 if (a.value.match(/^Mso/)) {
24876                     node.className = '';
24877                 }
24878             }
24879             
24880             // style cleanup!?
24881             // class cleanup?
24882             
24883         }
24884         
24885         
24886         this.cleanUpChildren(node);
24887         
24888         
24889     }
24890     
24891     
24892     // hide stuff that is not compatible
24893     /**
24894      * @event blur
24895      * @hide
24896      */
24897     /**
24898      * @event change
24899      * @hide
24900      */
24901     /**
24902      * @event focus
24903      * @hide
24904      */
24905     /**
24906      * @event specialkey
24907      * @hide
24908      */
24909     /**
24910      * @cfg {String} fieldClass @hide
24911      */
24912     /**
24913      * @cfg {String} focusClass @hide
24914      */
24915     /**
24916      * @cfg {String} autoCreate @hide
24917      */
24918     /**
24919      * @cfg {String} inputType @hide
24920      */
24921     /**
24922      * @cfg {String} invalidClass @hide
24923      */
24924     /**
24925      * @cfg {String} invalidText @hide
24926      */
24927     /**
24928      * @cfg {String} msgFx @hide
24929      */
24930     /**
24931      * @cfg {String} validateOnBlur @hide
24932      */
24933 });
24934
24935 Roo.form.HtmlEditor.white = [
24936         'area', 'br', 'img', 'input', 'hr', 'wbr',
24937         
24938        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24939        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24940        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24941        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24942        'table',   'ul',         'xmp', 
24943        
24944        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24945       'thead',   'tr', 
24946      
24947       'dir', 'menu', 'ol', 'ul', 'dl',
24948        
24949       'embed',  'object'
24950 ];
24951
24952
24953 Roo.form.HtmlEditor.black = [
24954     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24955         'applet', // 
24956         'base',   'basefont', 'bgsound', 'blink',  'body', 
24957         'frame',  'frameset', 'head',    'html',   'ilayer', 
24958         'iframe', 'layer',  'link',     'meta',    'object',   
24959         'script', 'style' ,'title',  'xml' // clean later..
24960 ];
24961 Roo.form.HtmlEditor.clean = [
24962     'script', 'style', 'title', 'xml'
24963 ];
24964
24965 // attributes..
24966
24967 Roo.form.HtmlEditor.ablack = [
24968     'on'
24969 ];
24970     
24971 Roo.form.HtmlEditor.aclean = [ 
24972     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24973 ];
24974
24975 // protocols..
24976 Roo.form.HtmlEditor.pwhite= [
24977         'http',  'https',  'mailto'
24978 ];
24979
24980 Roo.form.HtmlEditor.cwhite= [
24981         'text-align',
24982         'font-size'
24983 ];
24984
24985 // <script type="text/javascript">
24986 /*
24987  * Based on
24988  * Ext JS Library 1.1.1
24989  * Copyright(c) 2006-2007, Ext JS, LLC.
24990  *  
24991  
24992  */
24993
24994 /**
24995  * @class Roo.form.HtmlEditorToolbar1
24996  * Basic Toolbar
24997  * 
24998  * Usage:
24999  *
25000  new Roo.form.HtmlEditor({
25001     ....
25002     toolbars : [
25003         new Roo.form.HtmlEditorToolbar1({
25004             disable : { fonts: 1 , format: 1, ..., ... , ...],
25005             btns : [ .... ]
25006         })
25007     }
25008      
25009  * 
25010  * @cfg {Object} disable List of elements to disable..
25011  * @cfg {Array} btns List of additional buttons.
25012  * 
25013  * 
25014  * NEEDS Extra CSS? 
25015  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25016  */
25017  
25018 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25019 {
25020     
25021     Roo.apply(this, config);
25022     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25023     // dont call parent... till later.
25024 }
25025
25026 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25027     
25028     tb: false,
25029     
25030     rendered: false,
25031     
25032     editor : false,
25033     /**
25034      * @cfg {Object} disable  List of toolbar elements to disable
25035          
25036      */
25037     disable : false,
25038       /**
25039      * @cfg {Array} fontFamilies An array of available font families
25040      */
25041     fontFamilies : [
25042         'Arial',
25043         'Courier New',
25044         'Tahoma',
25045         'Times New Roman',
25046         'Verdana'
25047     ],
25048     
25049     specialChars : [
25050            "&#169;",
25051           "&#174;",     
25052           "&#8482;",    
25053           "&#163;" ,    
25054          // "&#8212;",    
25055           "&#8230;",    
25056           "&#247;" ,    
25057         //  "&#225;" ,     ?? a acute?
25058            "&#8364;"    , //Euro
25059        //   "&#8220;"    ,
25060         //  "&#8221;"    ,
25061         //  "&#8226;"    ,
25062           "&#176;"  //   , // degrees
25063
25064          // "&#233;"     , // e ecute
25065          // "&#250;"     , // u ecute?
25066     ],
25067     inputElements : [ 
25068             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25069             "input:submit", "input:button", "select", "textarea", "label" ],
25070     formats : [
25071         ["p"] ,  
25072         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25073         ["pre"],[ "code"], 
25074         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25075     ],
25076      /**
25077      * @cfg {String} defaultFont default font to use.
25078      */
25079     defaultFont: 'tahoma',
25080    
25081     fontSelect : false,
25082     
25083     
25084     formatCombo : false,
25085     
25086     init : function(editor)
25087     {
25088         this.editor = editor;
25089         
25090         
25091         var fid = editor.frameId;
25092         var etb = this;
25093         function btn(id, toggle, handler){
25094             var xid = fid + '-'+ id ;
25095             return {
25096                 id : xid,
25097                 cmd : id,
25098                 cls : 'x-btn-icon x-edit-'+id,
25099                 enableToggle:toggle !== false,
25100                 scope: editor, // was editor...
25101                 handler:handler||editor.relayBtnCmd,
25102                 clickEvent:'mousedown',
25103                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25104                 tabIndex:-1
25105             };
25106         }
25107         
25108         
25109         
25110         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25111         this.tb = tb;
25112          // stop form submits
25113         tb.el.on('click', function(e){
25114             e.preventDefault(); // what does this do?
25115         });
25116
25117         if(!this.disable.font && !Roo.isSafari){
25118             /* why no safari for fonts
25119             editor.fontSelect = tb.el.createChild({
25120                 tag:'select',
25121                 tabIndex: -1,
25122                 cls:'x-font-select',
25123                 html: editor.createFontOptions()
25124             });
25125             editor.fontSelect.on('change', function(){
25126                 var font = editor.fontSelect.dom.value;
25127                 editor.relayCmd('fontname', font);
25128                 editor.deferFocus();
25129             }, editor);
25130             tb.add(
25131                 editor.fontSelect.dom,
25132                 '-'
25133             );
25134             */
25135         };
25136         if(!this.disable.formats){
25137             this.formatCombo = new Roo.form.ComboBox({
25138                 store: new Roo.data.SimpleStore({
25139                     id : 'tag',
25140                     fields: ['tag'],
25141                     data : this.formats // from states.js
25142                 }),
25143                 blockFocus : true,
25144                 //autoCreate : {tag: "div",  size: "20"},
25145                 displayField:'tag',
25146                 typeAhead: false,
25147                 mode: 'local',
25148                 editable : false,
25149                 triggerAction: 'all',
25150                 emptyText:'Add tag',
25151                 selectOnFocus:true,
25152                 width:135,
25153                 listeners : {
25154                     'select': function(c, r, i) {
25155                         editor.insertTag(r.get('tag'));
25156                         editor.focus();
25157                     }
25158                 }
25159
25160             });
25161             tb.addField(this.formatCombo);
25162             
25163         }
25164         
25165         if(!this.disable.format){
25166             tb.add(
25167                 btn('bold'),
25168                 btn('italic'),
25169                 btn('underline')
25170             );
25171         };
25172         if(!this.disable.fontSize){
25173             tb.add(
25174                 '-',
25175                 
25176                 
25177                 btn('increasefontsize', false, editor.adjustFont),
25178                 btn('decreasefontsize', false, editor.adjustFont)
25179             );
25180         };
25181         
25182         
25183         if(this.disable.colors){
25184             tb.add(
25185                 '-', {
25186                     id:editor.frameId +'-forecolor',
25187                     cls:'x-btn-icon x-edit-forecolor',
25188                     clickEvent:'mousedown',
25189                     tooltip: this.buttonTips['forecolor'] || undefined,
25190                     tabIndex:-1,
25191                     menu : new Roo.menu.ColorMenu({
25192                         allowReselect: true,
25193                         focus: Roo.emptyFn,
25194                         value:'000000',
25195                         plain:true,
25196                         selectHandler: function(cp, color){
25197                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25198                             editor.deferFocus();
25199                         },
25200                         scope: editor,
25201                         clickEvent:'mousedown'
25202                     })
25203                 }, {
25204                     id:editor.frameId +'backcolor',
25205                     cls:'x-btn-icon x-edit-backcolor',
25206                     clickEvent:'mousedown',
25207                     tooltip: this.buttonTips['backcolor'] || undefined,
25208                     tabIndex:-1,
25209                     menu : new Roo.menu.ColorMenu({
25210                         focus: Roo.emptyFn,
25211                         value:'FFFFFF',
25212                         plain:true,
25213                         allowReselect: true,
25214                         selectHandler: function(cp, color){
25215                             if(Roo.isGecko){
25216                                 editor.execCmd('useCSS', false);
25217                                 editor.execCmd('hilitecolor', color);
25218                                 editor.execCmd('useCSS', true);
25219                                 editor.deferFocus();
25220                             }else{
25221                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25222                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25223                                 editor.deferFocus();
25224                             }
25225                         },
25226                         scope:editor,
25227                         clickEvent:'mousedown'
25228                     })
25229                 }
25230             );
25231         };
25232         // now add all the items...
25233         
25234
25235         if(!this.disable.alignments){
25236             tb.add(
25237                 '-',
25238                 btn('justifyleft'),
25239                 btn('justifycenter'),
25240                 btn('justifyright')
25241             );
25242         };
25243
25244         //if(!Roo.isSafari){
25245             if(!this.disable.links){
25246                 tb.add(
25247                     '-',
25248                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25249                 );
25250             };
25251
25252             if(!this.disable.lists){
25253                 tb.add(
25254                     '-',
25255                     btn('insertorderedlist'),
25256                     btn('insertunorderedlist')
25257                 );
25258             }
25259             if(!this.disable.sourceEdit){
25260                 tb.add(
25261                     '-',
25262                     btn('sourceedit', true, function(btn){
25263                         this.toggleSourceEdit(btn.pressed);
25264                     })
25265                 );
25266             }
25267         //}
25268         
25269         var smenu = { };
25270         // special menu.. - needs to be tidied up..
25271         if (!this.disable.special) {
25272             smenu = {
25273                 text: "&#169;",
25274                 cls: 'x-edit-none',
25275                 menu : {
25276                     items : []
25277                    }
25278             };
25279             for (var i =0; i < this.specialChars.length; i++) {
25280                 smenu.menu.items.push({
25281                     
25282                     html: this.specialChars[i],
25283                     handler: function(a,b) {
25284                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25285                         
25286                     },
25287                     tabIndex:-1
25288                 });
25289             }
25290             
25291             
25292             tb.add(smenu);
25293             
25294             
25295         }
25296         if (this.btns) {
25297             for(var i =0; i< this.btns.length;i++) {
25298                 var b = this.btns[i];
25299                 b.cls =  'x-edit-none';
25300                 b.scope = editor;
25301                 tb.add(b);
25302             }
25303         
25304         }
25305         
25306         
25307         
25308         // disable everything...
25309         
25310         this.tb.items.each(function(item){
25311            if(item.id != editor.frameId+ '-sourceedit'){
25312                 item.disable();
25313             }
25314         });
25315         this.rendered = true;
25316         
25317         // the all the btns;
25318         editor.on('editorevent', this.updateToolbar, this);
25319         // other toolbars need to implement this..
25320         //editor.on('editmodechange', this.updateToolbar, this);
25321     },
25322     
25323     
25324     
25325     /**
25326      * Protected method that will not generally be called directly. It triggers
25327      * a toolbar update by reading the markup state of the current selection in the editor.
25328      */
25329     updateToolbar: function(){
25330
25331         if(!this.editor.activated){
25332             this.editor.onFirstFocus();
25333             return;
25334         }
25335
25336         var btns = this.tb.items.map, 
25337             doc = this.editor.doc,
25338             frameId = this.editor.frameId;
25339
25340         if(!this.disable.font && !Roo.isSafari){
25341             /*
25342             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25343             if(name != this.fontSelect.dom.value){
25344                 this.fontSelect.dom.value = name;
25345             }
25346             */
25347         }
25348         if(!this.disable.format){
25349             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25350             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25351             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25352         }
25353         if(!this.disable.alignments){
25354             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25355             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25356             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25357         }
25358         if(!Roo.isSafari && !this.disable.lists){
25359             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25360             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25361         }
25362         
25363         var ans = this.editor.getAllAncestors();
25364         if (this.formatCombo) {
25365             
25366             
25367             var store = this.formatCombo.store;
25368             this.formatCombo.setValue("");
25369             for (var i =0; i < ans.length;i++) {
25370                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25371                     // select it..
25372                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25373                     break;
25374                 }
25375             }
25376         }
25377         
25378         
25379         
25380         // hides menus... - so this cant be on a menu...
25381         Roo.menu.MenuMgr.hideAll();
25382
25383         //this.editorsyncValue();
25384     },
25385    
25386     
25387     createFontOptions : function(){
25388         var buf = [], fs = this.fontFamilies, ff, lc;
25389         for(var i = 0, len = fs.length; i< len; i++){
25390             ff = fs[i];
25391             lc = ff.toLowerCase();
25392             buf.push(
25393                 '<option value="',lc,'" style="font-family:',ff,';"',
25394                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25395                     ff,
25396                 '</option>'
25397             );
25398         }
25399         return buf.join('');
25400     },
25401     
25402     toggleSourceEdit : function(sourceEditMode){
25403         if(sourceEditMode === undefined){
25404             sourceEditMode = !this.sourceEditMode;
25405         }
25406         this.sourceEditMode = sourceEditMode === true;
25407         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25408         // just toggle the button?
25409         if(btn.pressed !== this.editor.sourceEditMode){
25410             btn.toggle(this.editor.sourceEditMode);
25411             return;
25412         }
25413         
25414         if(this.sourceEditMode){
25415             this.tb.items.each(function(item){
25416                 if(item.cmd != 'sourceedit'){
25417                     item.disable();
25418                 }
25419             });
25420           
25421         }else{
25422             if(this.initialized){
25423                 this.tb.items.each(function(item){
25424                     item.enable();
25425                 });
25426             }
25427             
25428         }
25429         // tell the editor that it's been pressed..
25430         this.editor.toggleSourceEdit(sourceEditMode);
25431        
25432     },
25433      /**
25434      * Object collection of toolbar tooltips for the buttons in the editor. The key
25435      * is the command id associated with that button and the value is a valid QuickTips object.
25436      * For example:
25437 <pre><code>
25438 {
25439     bold : {
25440         title: 'Bold (Ctrl+B)',
25441         text: 'Make the selected text bold.',
25442         cls: 'x-html-editor-tip'
25443     },
25444     italic : {
25445         title: 'Italic (Ctrl+I)',
25446         text: 'Make the selected text italic.',
25447         cls: 'x-html-editor-tip'
25448     },
25449     ...
25450 </code></pre>
25451     * @type Object
25452      */
25453     buttonTips : {
25454         bold : {
25455             title: 'Bold (Ctrl+B)',
25456             text: 'Make the selected text bold.',
25457             cls: 'x-html-editor-tip'
25458         },
25459         italic : {
25460             title: 'Italic (Ctrl+I)',
25461             text: 'Make the selected text italic.',
25462             cls: 'x-html-editor-tip'
25463         },
25464         underline : {
25465             title: 'Underline (Ctrl+U)',
25466             text: 'Underline the selected text.',
25467             cls: 'x-html-editor-tip'
25468         },
25469         increasefontsize : {
25470             title: 'Grow Text',
25471             text: 'Increase the font size.',
25472             cls: 'x-html-editor-tip'
25473         },
25474         decreasefontsize : {
25475             title: 'Shrink Text',
25476             text: 'Decrease the font size.',
25477             cls: 'x-html-editor-tip'
25478         },
25479         backcolor : {
25480             title: 'Text Highlight Color',
25481             text: 'Change the background color of the selected text.',
25482             cls: 'x-html-editor-tip'
25483         },
25484         forecolor : {
25485             title: 'Font Color',
25486             text: 'Change the color of the selected text.',
25487             cls: 'x-html-editor-tip'
25488         },
25489         justifyleft : {
25490             title: 'Align Text Left',
25491             text: 'Align text to the left.',
25492             cls: 'x-html-editor-tip'
25493         },
25494         justifycenter : {
25495             title: 'Center Text',
25496             text: 'Center text in the editor.',
25497             cls: 'x-html-editor-tip'
25498         },
25499         justifyright : {
25500             title: 'Align Text Right',
25501             text: 'Align text to the right.',
25502             cls: 'x-html-editor-tip'
25503         },
25504         insertunorderedlist : {
25505             title: 'Bullet List',
25506             text: 'Start a bulleted list.',
25507             cls: 'x-html-editor-tip'
25508         },
25509         insertorderedlist : {
25510             title: 'Numbered List',
25511             text: 'Start a numbered list.',
25512             cls: 'x-html-editor-tip'
25513         },
25514         createlink : {
25515             title: 'Hyperlink',
25516             text: 'Make the selected text a hyperlink.',
25517             cls: 'x-html-editor-tip'
25518         },
25519         sourceedit : {
25520             title: 'Source Edit',
25521             text: 'Switch to source editing mode.',
25522             cls: 'x-html-editor-tip'
25523         }
25524     },
25525     // private
25526     onDestroy : function(){
25527         if(this.rendered){
25528             
25529             this.tb.items.each(function(item){
25530                 if(item.menu){
25531                     item.menu.removeAll();
25532                     if(item.menu.el){
25533                         item.menu.el.destroy();
25534                     }
25535                 }
25536                 item.destroy();
25537             });
25538              
25539         }
25540     },
25541     onFirstFocus: function() {
25542         this.tb.items.each(function(item){
25543            item.enable();
25544         });
25545     }
25546 });
25547
25548
25549
25550
25551 // <script type="text/javascript">
25552 /*
25553  * Based on
25554  * Ext JS Library 1.1.1
25555  * Copyright(c) 2006-2007, Ext JS, LLC.
25556  *  
25557  
25558  */
25559
25560  
25561 /**
25562  * @class Roo.form.HtmlEditor.ToolbarContext
25563  * Context Toolbar
25564  * 
25565  * Usage:
25566  *
25567  new Roo.form.HtmlEditor({
25568     ....
25569     toolbars : [
25570         new Roo.form.HtmlEditor.ToolbarStandard(),
25571         new Roo.form.HtmlEditor.ToolbarContext()
25572         })
25573     }
25574      
25575  * 
25576  * @config : {Object} disable List of elements to disable.. (not done yet.)
25577  * 
25578  * 
25579  */
25580
25581 Roo.form.HtmlEditor.ToolbarContext = function(config)
25582 {
25583     
25584     Roo.apply(this, config);
25585     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25586     // dont call parent... till later.
25587 }
25588 Roo.form.HtmlEditor.ToolbarContext.types = {
25589     'IMG' : {
25590         width : {
25591             title: "Width",
25592             width: 40
25593         },
25594         height:  {
25595             title: "Height",
25596             width: 40
25597         },
25598         align: {
25599             title: "Align",
25600             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25601             width : 80
25602             
25603         },
25604         border: {
25605             title: "Border",
25606             width: 40
25607         },
25608         alt: {
25609             title: "Alt",
25610             width: 120
25611         },
25612         src : {
25613             title: "Src",
25614             width: 220
25615         }
25616         
25617     },
25618     'A' : {
25619         name : {
25620             title: "Name",
25621             width: 50
25622         },
25623         href:  {
25624             title: "Href",
25625             width: 220
25626         } // border?
25627         
25628     },
25629     'TABLE' : {
25630         rows : {
25631             title: "Rows",
25632             width: 20
25633         },
25634         cols : {
25635             title: "Cols",
25636             width: 20
25637         },
25638         width : {
25639             title: "Width",
25640             width: 40
25641         },
25642         height : {
25643             title: "Height",
25644             width: 40
25645         },
25646         border : {
25647             title: "Border",
25648             width: 20
25649         }
25650     },
25651     'TD' : {
25652         width : {
25653             title: "Width",
25654             width: 40
25655         },
25656         height : {
25657             title: "Height",
25658             width: 40
25659         },   
25660         align: {
25661             title: "Align",
25662             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25663             width: 40
25664         },
25665         valign: {
25666             title: "Valign",
25667             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25668             width: 40
25669         },
25670         colspan: {
25671             title: "Colspan",
25672             width: 20
25673             
25674         }
25675     },
25676     'INPUT' : {
25677         name : {
25678             title: "name",
25679             width: 120
25680         },
25681         value : {
25682             title: "Value",
25683             width: 120
25684         },
25685         width : {
25686             title: "Width",
25687             width: 40
25688         }
25689     },
25690     'LABEL' : {
25691         'for' : {
25692             title: "For",
25693             width: 120
25694         }
25695     },
25696     'TEXTAREA' : {
25697           name : {
25698             title: "name",
25699             width: 120
25700         },
25701         rows : {
25702             title: "Rows",
25703             width: 20
25704         },
25705         cols : {
25706             title: "Cols",
25707             width: 20
25708         }
25709     },
25710     'SELECT' : {
25711         name : {
25712             title: "name",
25713             width: 120
25714         },
25715         selectoptions : {
25716             title: "Options",
25717             width: 200
25718         }
25719     },
25720     'BODY' : {
25721         title : {
25722             title: "title",
25723             width: 120,
25724             disabled : true
25725         }
25726     }
25727 };
25728
25729
25730
25731 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25732     
25733     tb: false,
25734     
25735     rendered: false,
25736     
25737     editor : false,
25738     /**
25739      * @cfg {Object} disable  List of toolbar elements to disable
25740          
25741      */
25742     disable : false,
25743     
25744     
25745     
25746     toolbars : false,
25747     
25748     init : function(editor)
25749     {
25750         this.editor = editor;
25751         
25752         
25753         var fid = editor.frameId;
25754         var etb = this;
25755         function btn(id, toggle, handler){
25756             var xid = fid + '-'+ id ;
25757             return {
25758                 id : xid,
25759                 cmd : id,
25760                 cls : 'x-btn-icon x-edit-'+id,
25761                 enableToggle:toggle !== false,
25762                 scope: editor, // was editor...
25763                 handler:handler||editor.relayBtnCmd,
25764                 clickEvent:'mousedown',
25765                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25766                 tabIndex:-1
25767             };
25768         }
25769         // create a new element.
25770         var wdiv = editor.wrap.createChild({
25771                 tag: 'div'
25772             }, editor.wrap.dom.firstChild.nextSibling, true);
25773         
25774         // can we do this more than once??
25775         
25776          // stop form submits
25777       
25778  
25779         // disable everything...
25780         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25781         this.toolbars = {};
25782            
25783         for (var i in  ty) {
25784           
25785             this.toolbars[i] = this.buildToolbar(ty[i],i);
25786         }
25787         this.tb = this.toolbars.BODY;
25788         this.tb.el.show();
25789         
25790          
25791         this.rendered = true;
25792         
25793         // the all the btns;
25794         editor.on('editorevent', this.updateToolbar, this);
25795         // other toolbars need to implement this..
25796         //editor.on('editmodechange', this.updateToolbar, this);
25797     },
25798     
25799     
25800     
25801     /**
25802      * Protected method that will not generally be called directly. It triggers
25803      * a toolbar update by reading the markup state of the current selection in the editor.
25804      */
25805     updateToolbar: function(){
25806
25807         if(!this.editor.activated){
25808             this.editor.onFirstFocus();
25809             return;
25810         }
25811
25812         
25813         var ans = this.editor.getAllAncestors();
25814         
25815         // pick
25816         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25817         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25818         sel = sel ? sel : this.editor.doc.body;
25819         sel = sel.tagName.length ? sel : this.editor.doc.body;
25820         var tn = sel.tagName.toUpperCase();
25821         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25822         tn = sel.tagName.toUpperCase();
25823         if (this.tb.name  == tn) {
25824             return; // no change
25825         }
25826         this.tb.el.hide();
25827         ///console.log("show: " + tn);
25828         this.tb =  this.toolbars[tn];
25829         this.tb.el.show();
25830         this.tb.fields.each(function(e) {
25831             e.setValue(sel.getAttribute(e.name));
25832         });
25833         this.tb.selectedNode = sel;
25834         
25835         
25836         Roo.menu.MenuMgr.hideAll();
25837
25838         //this.editorsyncValue();
25839     },
25840    
25841        
25842     // private
25843     onDestroy : function(){
25844         if(this.rendered){
25845             
25846             this.tb.items.each(function(item){
25847                 if(item.menu){
25848                     item.menu.removeAll();
25849                     if(item.menu.el){
25850                         item.menu.el.destroy();
25851                     }
25852                 }
25853                 item.destroy();
25854             });
25855              
25856         }
25857     },
25858     onFirstFocus: function() {
25859         // need to do this for all the toolbars..
25860         this.tb.items.each(function(item){
25861            item.enable();
25862         });
25863     },
25864     buildToolbar: function(tlist, nm)
25865     {
25866         var editor = this.editor;
25867          // create a new element.
25868         var wdiv = editor.wrap.createChild({
25869                 tag: 'div'
25870             }, editor.wrap.dom.firstChild.nextSibling, true);
25871         
25872        
25873         var tb = new Roo.Toolbar(wdiv);
25874         tb.add(nm+ ":&nbsp;");
25875         for (var i in tlist) {
25876             var item = tlist[i];
25877             tb.add(item.title + ":&nbsp;");
25878             if (item.opts) {
25879                 // fixme
25880                 
25881               
25882                 tb.addField( new Roo.form.ComboBox({
25883                     store: new Roo.data.SimpleStore({
25884                         id : 'val',
25885                         fields: ['val'],
25886                         data : item.opts // from states.js
25887                     }),
25888                     name : i,
25889                     displayField:'val',
25890                     typeAhead: false,
25891                     mode: 'local',
25892                     editable : false,
25893                     triggerAction: 'all',
25894                     emptyText:'Select',
25895                     selectOnFocus:true,
25896                     width: item.width ? item.width  : 130,
25897                     listeners : {
25898                         'select': function(c, r, i) {
25899                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25900                         }
25901                     }
25902
25903                 }));
25904                 continue;
25905                     
25906                 
25907                 
25908                 
25909                 
25910                 tb.addField( new Roo.form.TextField({
25911                     name: i,
25912                     width: 100,
25913                     //allowBlank:false,
25914                     value: ''
25915                 }));
25916                 continue;
25917             }
25918             tb.addField( new Roo.form.TextField({
25919                 name: i,
25920                 width: item.width,
25921                 //allowBlank:true,
25922                 value: '',
25923                 listeners: {
25924                     'change' : function(f, nv, ov) {
25925                         tb.selectedNode.setAttribute(f.name, nv);
25926                     }
25927                 }
25928             }));
25929              
25930         }
25931         tb.el.on('click', function(e){
25932             e.preventDefault(); // what does this do?
25933         });
25934         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25935         tb.el.hide();
25936         tb.name = nm;
25937         // dont need to disable them... as they will get hidden
25938         return tb;
25939          
25940         
25941     }
25942     
25943     
25944     
25945     
25946 });
25947
25948
25949
25950
25951
25952 /*
25953  * Based on:
25954  * Ext JS Library 1.1.1
25955  * Copyright(c) 2006-2007, Ext JS, LLC.
25956  *
25957  * Originally Released Under LGPL - original licence link has changed is not relivant.
25958  *
25959  * Fork - LGPL
25960  * <script type="text/javascript">
25961  */
25962  
25963 /**
25964  * @class Roo.form.BasicForm
25965  * @extends Roo.util.Observable
25966  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25967  * @constructor
25968  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25969  * @param {Object} config Configuration options
25970  */
25971 Roo.form.BasicForm = function(el, config){
25972     this.allItems = [];
25973     this.childForms = [];
25974     Roo.apply(this, config);
25975     /*
25976      * The Roo.form.Field items in this form.
25977      * @type MixedCollection
25978      */
25979      
25980      
25981     this.items = new Roo.util.MixedCollection(false, function(o){
25982         return o.id || (o.id = Roo.id());
25983     });
25984     this.addEvents({
25985         /**
25986          * @event beforeaction
25987          * Fires before any action is performed. Return false to cancel the action.
25988          * @param {Form} this
25989          * @param {Action} action The action to be performed
25990          */
25991         beforeaction: true,
25992         /**
25993          * @event actionfailed
25994          * Fires when an action fails.
25995          * @param {Form} this
25996          * @param {Action} action The action that failed
25997          */
25998         actionfailed : true,
25999         /**
26000          * @event actioncomplete
26001          * Fires when an action is completed.
26002          * @param {Form} this
26003          * @param {Action} action The action that completed
26004          */
26005         actioncomplete : true
26006     });
26007     if(el){
26008         this.initEl(el);
26009     }
26010     Roo.form.BasicForm.superclass.constructor.call(this);
26011 };
26012
26013 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26014     /**
26015      * @cfg {String} method
26016      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26017      */
26018     /**
26019      * @cfg {DataReader} reader
26020      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26021      * This is optional as there is built-in support for processing JSON.
26022      */
26023     /**
26024      * @cfg {DataReader} errorReader
26025      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26026      * This is completely optional as there is built-in support for processing JSON.
26027      */
26028     /**
26029      * @cfg {String} url
26030      * The URL to use for form actions if one isn't supplied in the action options.
26031      */
26032     /**
26033      * @cfg {Boolean} fileUpload
26034      * Set to true if this form is a file upload.
26035      */
26036      
26037     /**
26038      * @cfg {Object} baseParams
26039      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26040      */
26041      /**
26042      
26043     /**
26044      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26045      */
26046     timeout: 30,
26047
26048     // private
26049     activeAction : null,
26050
26051     /**
26052      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26053      * or setValues() data instead of when the form was first created.
26054      */
26055     trackResetOnLoad : false,
26056     
26057     
26058     /**
26059      * childForms - used for multi-tab forms
26060      * @type {Array}
26061      */
26062     childForms : false,
26063     
26064     /**
26065      * allItems - full list of fields.
26066      * @type {Array}
26067      */
26068     allItems : false,
26069     
26070     /**
26071      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26072      * element by passing it or its id or mask the form itself by passing in true.
26073      * @type Mixed
26074      */
26075     waitMsgTarget : false,
26076
26077     // private
26078     initEl : function(el){
26079         this.el = Roo.get(el);
26080         this.id = this.el.id || Roo.id();
26081         this.el.on('submit', this.onSubmit, this);
26082         this.el.addClass('x-form');
26083     },
26084
26085     // private
26086     onSubmit : function(e){
26087         e.stopEvent();
26088     },
26089
26090     /**
26091      * Returns true if client-side validation on the form is successful.
26092      * @return Boolean
26093      */
26094     isValid : function(){
26095         var valid = true;
26096         this.items.each(function(f){
26097            if(!f.validate()){
26098                valid = false;
26099            }
26100         });
26101         return valid;
26102     },
26103
26104     /**
26105      * Returns true if any fields in this form have changed since their original load.
26106      * @return Boolean
26107      */
26108     isDirty : function(){
26109         var dirty = false;
26110         this.items.each(function(f){
26111            if(f.isDirty()){
26112                dirty = true;
26113                return false;
26114            }
26115         });
26116         return dirty;
26117     },
26118
26119     /**
26120      * Performs a predefined action (submit or load) or custom actions you define on this form.
26121      * @param {String} actionName The name of the action type
26122      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26123      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26124      * accept other config options):
26125      * <pre>
26126 Property          Type             Description
26127 ----------------  ---------------  ----------------------------------------------------------------------------------
26128 url               String           The url for the action (defaults to the form's url)
26129 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26130 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26131 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26132                                    validate the form on the client (defaults to false)
26133      * </pre>
26134      * @return {BasicForm} this
26135      */
26136     doAction : function(action, options){
26137         if(typeof action == 'string'){
26138             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26139         }
26140         if(this.fireEvent('beforeaction', this, action) !== false){
26141             this.beforeAction(action);
26142             action.run.defer(100, action);
26143         }
26144         return this;
26145     },
26146
26147     /**
26148      * Shortcut to do a submit action.
26149      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26150      * @return {BasicForm} this
26151      */
26152     submit : function(options){
26153         this.doAction('submit', options);
26154         return this;
26155     },
26156
26157     /**
26158      * Shortcut to do a load action.
26159      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26160      * @return {BasicForm} this
26161      */
26162     load : function(options){
26163         this.doAction('load', options);
26164         return this;
26165     },
26166
26167     /**
26168      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26169      * @param {Record} record The record to edit
26170      * @return {BasicForm} this
26171      */
26172     updateRecord : function(record){
26173         record.beginEdit();
26174         var fs = record.fields;
26175         fs.each(function(f){
26176             var field = this.findField(f.name);
26177             if(field){
26178                 record.set(f.name, field.getValue());
26179             }
26180         }, this);
26181         record.endEdit();
26182         return this;
26183     },
26184
26185     /**
26186      * Loads an Roo.data.Record into this form.
26187      * @param {Record} record The record to load
26188      * @return {BasicForm} this
26189      */
26190     loadRecord : function(record){
26191         this.setValues(record.data);
26192         return this;
26193     },
26194
26195     // private
26196     beforeAction : function(action){
26197         var o = action.options;
26198         
26199        
26200         if(this.waitMsgTarget === true){
26201             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26202         }else if(this.waitMsgTarget){
26203             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26204             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26205         }else {
26206             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26207         }
26208          
26209     },
26210
26211     // private
26212     afterAction : function(action, success){
26213         this.activeAction = null;
26214         var o = action.options;
26215         
26216         if(this.waitMsgTarget === true){
26217             this.el.unmask();
26218         }else if(this.waitMsgTarget){
26219             this.waitMsgTarget.unmask();
26220         }else{
26221             Roo.MessageBox.updateProgress(1);
26222             Roo.MessageBox.hide();
26223         }
26224          
26225         if(success){
26226             if(o.reset){
26227                 this.reset();
26228             }
26229             Roo.callback(o.success, o.scope, [this, action]);
26230             this.fireEvent('actioncomplete', this, action);
26231             
26232         }else{
26233             Roo.callback(o.failure, o.scope, [this, action]);
26234             // show an error message if no failed handler is set..
26235             if (!this.hasListener('actionfailed')) {
26236                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
26237             }
26238             
26239             this.fireEvent('actionfailed', this, action);
26240         }
26241         
26242     },
26243
26244     /**
26245      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26246      * @param {String} id The value to search for
26247      * @return Field
26248      */
26249     findField : function(id){
26250         var field = this.items.get(id);
26251         if(!field){
26252             this.items.each(function(f){
26253                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26254                     field = f;
26255                     return false;
26256                 }
26257             });
26258         }
26259         return field || null;
26260     },
26261
26262     /**
26263      * Add a secondary form to this one, 
26264      * Used to provide tabbed forms. One form is primary, with hidden values 
26265      * which mirror the elements from the other forms.
26266      * 
26267      * @param {Roo.form.Form} form to add.
26268      * 
26269      */
26270     addForm : function(form)
26271     {
26272        
26273         if (this.childForms.indexOf(form) > -1) {
26274             // already added..
26275             return;
26276         }
26277         this.childForms.push(form);
26278         var n = '';
26279         Roo.each(form.allItems, function (fe) {
26280             
26281             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26282             if (this.findField(n)) { // already added..
26283                 return;
26284             }
26285             var add = new Roo.form.Hidden({
26286                 name : n
26287             });
26288             add.render(this.el);
26289             
26290             this.add( add );
26291         }, this);
26292         
26293     },
26294     /**
26295      * Mark fields in this form invalid in bulk.
26296      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26297      * @return {BasicForm} this
26298      */
26299     markInvalid : function(errors){
26300         if(errors instanceof Array){
26301             for(var i = 0, len = errors.length; i < len; i++){
26302                 var fieldError = errors[i];
26303                 var f = this.findField(fieldError.id);
26304                 if(f){
26305                     f.markInvalid(fieldError.msg);
26306                 }
26307             }
26308         }else{
26309             var field, id;
26310             for(id in errors){
26311                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26312                     field.markInvalid(errors[id]);
26313                 }
26314             }
26315         }
26316         Roo.each(this.childForms || [], function (f) {
26317             f.markInvalid(errors);
26318         });
26319         
26320         return this;
26321     },
26322
26323     /**
26324      * Set values for fields in this form in bulk.
26325      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26326      * @return {BasicForm} this
26327      */
26328     setValues : function(values){
26329         if(values instanceof Array){ // array of objects
26330             for(var i = 0, len = values.length; i < len; i++){
26331                 var v = values[i];
26332                 var f = this.findField(v.id);
26333                 if(f){
26334                     f.setValue(v.value);
26335                     if(this.trackResetOnLoad){
26336                         f.originalValue = f.getValue();
26337                     }
26338                 }
26339             }
26340         }else{ // object hash
26341             var field, id;
26342             for(id in values){
26343                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26344                     
26345                     if (field.setFromData && 
26346                         field.valueField && 
26347                         field.displayField &&
26348                         // combos' with local stores can 
26349                         // be queried via setValue()
26350                         // to set their value..
26351                         (field.store && !field.store.isLocal)
26352                         ) {
26353                         // it's a combo
26354                         var sd = { };
26355                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26356                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26357                         field.setFromData(sd);
26358                         
26359                     } else {
26360                         field.setValue(values[id]);
26361                     }
26362                     
26363                     
26364                     if(this.trackResetOnLoad){
26365                         field.originalValue = field.getValue();
26366                     }
26367                 }
26368             }
26369         }
26370          
26371         Roo.each(this.childForms || [], function (f) {
26372             f.setValues(values);
26373         });
26374                 
26375         return this;
26376     },
26377
26378     /**
26379      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26380      * they are returned as an array.
26381      * @param {Boolean} asString
26382      * @return {Object}
26383      */
26384     getValues : function(asString){
26385         if (this.childForms) {
26386             // copy values from the child forms
26387             Roo.each(this.childForms, function (f) {
26388                 this.setValues(f.getValues());
26389             }, this);
26390         }
26391         
26392         
26393         
26394         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26395         if(asString === true){
26396             return fs;
26397         }
26398         return Roo.urlDecode(fs);
26399     },
26400     
26401     /**
26402      * Returns the fields in this form as an object with key/value pairs. 
26403      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26404      * @return {Object}
26405      */
26406     getFieldValues : function()
26407     {
26408         if (this.childForms) {
26409             // copy values from the child forms
26410             Roo.each(this.childForms, function (f) {
26411                 this.setValues(f.getValues());
26412             }, this);
26413         }
26414         
26415         var ret = {};
26416         this.items.each(function(f){
26417             if (!f.getName()) {
26418                 return;
26419             }
26420             var v = f.getValue();
26421             if ((typeof(v) == 'object') && f.getRawValue) {
26422                 v = f.getRawValue() ; // dates..
26423             }
26424             ret[f.getName()] = v;
26425         });
26426         
26427         return ret;
26428     },
26429
26430     /**
26431      * Clears all invalid messages in this form.
26432      * @return {BasicForm} this
26433      */
26434     clearInvalid : function(){
26435         this.items.each(function(f){
26436            f.clearInvalid();
26437         });
26438         
26439         Roo.each(this.childForms || [], function (f) {
26440             f.clearInvalid();
26441         });
26442         
26443         
26444         return this;
26445     },
26446
26447     /**
26448      * Resets this form.
26449      * @return {BasicForm} this
26450      */
26451     reset : function(){
26452         this.items.each(function(f){
26453             f.reset();
26454         });
26455         
26456         Roo.each(this.childForms || [], function (f) {
26457             f.reset();
26458         });
26459        
26460         
26461         return this;
26462     },
26463
26464     /**
26465      * Add Roo.form components to this form.
26466      * @param {Field} field1
26467      * @param {Field} field2 (optional)
26468      * @param {Field} etc (optional)
26469      * @return {BasicForm} this
26470      */
26471     add : function(){
26472         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26473         return this;
26474     },
26475
26476
26477     /**
26478      * Removes a field from the items collection (does NOT remove its markup).
26479      * @param {Field} field
26480      * @return {BasicForm} this
26481      */
26482     remove : function(field){
26483         this.items.remove(field);
26484         return this;
26485     },
26486
26487     /**
26488      * Looks at the fields in this form, checks them for an id attribute,
26489      * and calls applyTo on the existing dom element with that id.
26490      * @return {BasicForm} this
26491      */
26492     render : function(){
26493         this.items.each(function(f){
26494             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26495                 f.applyTo(f.id);
26496             }
26497         });
26498         return this;
26499     },
26500
26501     /**
26502      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26503      * @param {Object} values
26504      * @return {BasicForm} this
26505      */
26506     applyToFields : function(o){
26507         this.items.each(function(f){
26508            Roo.apply(f, o);
26509         });
26510         return this;
26511     },
26512
26513     /**
26514      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26515      * @param {Object} values
26516      * @return {BasicForm} this
26517      */
26518     applyIfToFields : function(o){
26519         this.items.each(function(f){
26520            Roo.applyIf(f, o);
26521         });
26522         return this;
26523     }
26524 });
26525
26526 // back compat
26527 Roo.BasicForm = Roo.form.BasicForm;/*
26528  * Based on:
26529  * Ext JS Library 1.1.1
26530  * Copyright(c) 2006-2007, Ext JS, LLC.
26531  *
26532  * Originally Released Under LGPL - original licence link has changed is not relivant.
26533  *
26534  * Fork - LGPL
26535  * <script type="text/javascript">
26536  */
26537
26538 /**
26539  * @class Roo.form.Form
26540  * @extends Roo.form.BasicForm
26541  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26542  * @constructor
26543  * @param {Object} config Configuration options
26544  */
26545 Roo.form.Form = function(config){
26546     var xitems =  [];
26547     if (config.items) {
26548         xitems = config.items;
26549         delete config.items;
26550     }
26551    
26552     
26553     Roo.form.Form.superclass.constructor.call(this, null, config);
26554     this.url = this.url || this.action;
26555     if(!this.root){
26556         this.root = new Roo.form.Layout(Roo.applyIf({
26557             id: Roo.id()
26558         }, config));
26559     }
26560     this.active = this.root;
26561     /**
26562      * Array of all the buttons that have been added to this form via {@link addButton}
26563      * @type Array
26564      */
26565     this.buttons = [];
26566     this.allItems = [];
26567     this.addEvents({
26568         /**
26569          * @event clientvalidation
26570          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26571          * @param {Form} this
26572          * @param {Boolean} valid true if the form has passed client-side validation
26573          */
26574         clientvalidation: true,
26575         /**
26576          * @event rendered
26577          * Fires when the form is rendered
26578          * @param {Roo.form.Form} form
26579          */
26580         rendered : true
26581     });
26582     
26583     if (this.progressUrl) {
26584             // push a hidden field onto the list of fields..
26585             this.addxtype( {
26586                     xns: Roo.form, 
26587                     xtype : 'Hidden', 
26588                     name : 'UPLOAD_IDENTIFIER' 
26589             });
26590         }
26591         
26592     
26593     Roo.each(xitems, this.addxtype, this);
26594     
26595     
26596     
26597 };
26598
26599 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26600     /**
26601      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26602      */
26603     /**
26604      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26605      */
26606     /**
26607      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26608      */
26609     buttonAlign:'center',
26610
26611     /**
26612      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26613      */
26614     minButtonWidth:75,
26615
26616     /**
26617      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26618      * This property cascades to child containers if not set.
26619      */
26620     labelAlign:'left',
26621
26622     /**
26623      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26624      * fires a looping event with that state. This is required to bind buttons to the valid
26625      * state using the config value formBind:true on the button.
26626      */
26627     monitorValid : false,
26628
26629     /**
26630      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26631      */
26632     monitorPoll : 200,
26633     
26634     /**
26635      * @cfg {String} progressUrl - Url to return progress data 
26636      */
26637     
26638     progressUrl : false,
26639   
26640     /**
26641      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26642      * fields are added and the column is closed. If no fields are passed the column remains open
26643      * until end() is called.
26644      * @param {Object} config The config to pass to the column
26645      * @param {Field} field1 (optional)
26646      * @param {Field} field2 (optional)
26647      * @param {Field} etc (optional)
26648      * @return Column The column container object
26649      */
26650     column : function(c){
26651         var col = new Roo.form.Column(c);
26652         this.start(col);
26653         if(arguments.length > 1){ // duplicate code required because of Opera
26654             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26655             this.end();
26656         }
26657         return col;
26658     },
26659
26660     /**
26661      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26662      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26663      * until end() is called.
26664      * @param {Object} config The config to pass to the fieldset
26665      * @param {Field} field1 (optional)
26666      * @param {Field} field2 (optional)
26667      * @param {Field} etc (optional)
26668      * @return FieldSet The fieldset container object
26669      */
26670     fieldset : function(c){
26671         var fs = new Roo.form.FieldSet(c);
26672         this.start(fs);
26673         if(arguments.length > 1){ // duplicate code required because of Opera
26674             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26675             this.end();
26676         }
26677         return fs;
26678     },
26679
26680     /**
26681      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26682      * fields are added and the container is closed. If no fields are passed the container remains open
26683      * until end() is called.
26684      * @param {Object} config The config to pass to the Layout
26685      * @param {Field} field1 (optional)
26686      * @param {Field} field2 (optional)
26687      * @param {Field} etc (optional)
26688      * @return Layout The container object
26689      */
26690     container : function(c){
26691         var l = new Roo.form.Layout(c);
26692         this.start(l);
26693         if(arguments.length > 1){ // duplicate code required because of Opera
26694             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26695             this.end();
26696         }
26697         return l;
26698     },
26699
26700     /**
26701      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26702      * @param {Object} container A Roo.form.Layout or subclass of Layout
26703      * @return {Form} this
26704      */
26705     start : function(c){
26706         // cascade label info
26707         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26708         this.active.stack.push(c);
26709         c.ownerCt = this.active;
26710         this.active = c;
26711         return this;
26712     },
26713
26714     /**
26715      * Closes the current open container
26716      * @return {Form} this
26717      */
26718     end : function(){
26719         if(this.active == this.root){
26720             return this;
26721         }
26722         this.active = this.active.ownerCt;
26723         return this;
26724     },
26725
26726     /**
26727      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26728      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26729      * as the label of the field.
26730      * @param {Field} field1
26731      * @param {Field} field2 (optional)
26732      * @param {Field} etc. (optional)
26733      * @return {Form} this
26734      */
26735     add : function(){
26736         this.active.stack.push.apply(this.active.stack, arguments);
26737         this.allItems.push.apply(this.allItems,arguments);
26738         var r = [];
26739         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26740             if(a[i].isFormField){
26741                 r.push(a[i]);
26742             }
26743         }
26744         if(r.length > 0){
26745             Roo.form.Form.superclass.add.apply(this, r);
26746         }
26747         return this;
26748     },
26749     
26750
26751     
26752     
26753     
26754      /**
26755      * Find any element that has been added to a form, using it's ID or name
26756      * This can include framesets, columns etc. along with regular fields..
26757      * @param {String} id - id or name to find.
26758      
26759      * @return {Element} e - or false if nothing found.
26760      */
26761     findbyId : function(id)
26762     {
26763         var ret = false;
26764         if (!id) {
26765             return ret;
26766         }
26767         Roo.each(this.allItems, function(f){
26768             if (f.id == id || f.name == id ){
26769                 ret = f;
26770                 return false;
26771             }
26772         });
26773         return ret;
26774     },
26775
26776     
26777     
26778     /**
26779      * Render this form into the passed container. This should only be called once!
26780      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26781      * @return {Form} this
26782      */
26783     render : function(ct)
26784     {
26785         
26786         
26787         
26788         ct = Roo.get(ct);
26789         var o = this.autoCreate || {
26790             tag: 'form',
26791             method : this.method || 'POST',
26792             id : this.id || Roo.id()
26793         };
26794         this.initEl(ct.createChild(o));
26795
26796         this.root.render(this.el);
26797         
26798        
26799              
26800         this.items.each(function(f){
26801             f.render('x-form-el-'+f.id);
26802         });
26803
26804         if(this.buttons.length > 0){
26805             // tables are required to maintain order and for correct IE layout
26806             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26807                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26808                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26809             }}, null, true);
26810             var tr = tb.getElementsByTagName('tr')[0];
26811             for(var i = 0, len = this.buttons.length; i < len; i++) {
26812                 var b = this.buttons[i];
26813                 var td = document.createElement('td');
26814                 td.className = 'x-form-btn-td';
26815                 b.render(tr.appendChild(td));
26816             }
26817         }
26818         if(this.monitorValid){ // initialize after render
26819             this.startMonitoring();
26820         }
26821         this.fireEvent('rendered', this);
26822         return this;
26823     },
26824
26825     /**
26826      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26827      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26828      * object or a valid Roo.DomHelper element config
26829      * @param {Function} handler The function called when the button is clicked
26830      * @param {Object} scope (optional) The scope of the handler function
26831      * @return {Roo.Button}
26832      */
26833     addButton : function(config, handler, scope){
26834         var bc = {
26835             handler: handler,
26836             scope: scope,
26837             minWidth: this.minButtonWidth,
26838             hideParent:true
26839         };
26840         if(typeof config == "string"){
26841             bc.text = config;
26842         }else{
26843             Roo.apply(bc, config);
26844         }
26845         var btn = new Roo.Button(null, bc);
26846         this.buttons.push(btn);
26847         return btn;
26848     },
26849
26850      /**
26851      * Adds a series of form elements (using the xtype property as the factory method.
26852      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26853      * @param {Object} config 
26854      */
26855     
26856     addxtype : function()
26857     {
26858         var ar = Array.prototype.slice.call(arguments, 0);
26859         var ret = false;
26860         for(var i = 0; i < ar.length; i++) {
26861             if (!ar[i]) {
26862                 continue; // skip -- if this happends something invalid got sent, we 
26863                 // should ignore it, as basically that interface element will not show up
26864                 // and that should be pretty obvious!!
26865             }
26866             
26867             if (Roo.form[ar[i].xtype]) {
26868                 ar[i].form = this;
26869                 var fe = Roo.factory(ar[i], Roo.form);
26870                 if (!ret) {
26871                     ret = fe;
26872                 }
26873                 fe.form = this;
26874                 if (fe.store) {
26875                     fe.store.form = this;
26876                 }
26877                 if (fe.isLayout) {  
26878                          
26879                     this.start(fe);
26880                     this.allItems.push(fe);
26881                     if (fe.items && fe.addxtype) {
26882                         fe.addxtype.apply(fe, fe.items);
26883                         delete fe.items;
26884                     }
26885                      this.end();
26886                     continue;
26887                 }
26888                 
26889                 
26890                  
26891                 this.add(fe);
26892               //  console.log('adding ' + ar[i].xtype);
26893             }
26894             if (ar[i].xtype == 'Button') {  
26895                 //console.log('adding button');
26896                 //console.log(ar[i]);
26897                 this.addButton(ar[i]);
26898                 this.allItems.push(fe);
26899                 continue;
26900             }
26901             
26902             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26903                 alert('end is not supported on xtype any more, use items');
26904             //    this.end();
26905             //    //console.log('adding end');
26906             }
26907             
26908         }
26909         return ret;
26910     },
26911     
26912     /**
26913      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26914      * option "monitorValid"
26915      */
26916     startMonitoring : function(){
26917         if(!this.bound){
26918             this.bound = true;
26919             Roo.TaskMgr.start({
26920                 run : this.bindHandler,
26921                 interval : this.monitorPoll || 200,
26922                 scope: this
26923             });
26924         }
26925     },
26926
26927     /**
26928      * Stops monitoring of the valid state of this form
26929      */
26930     stopMonitoring : function(){
26931         this.bound = false;
26932     },
26933
26934     // private
26935     bindHandler : function(){
26936         if(!this.bound){
26937             return false; // stops binding
26938         }
26939         var valid = true;
26940         this.items.each(function(f){
26941             if(!f.isValid(true)){
26942                 valid = false;
26943                 return false;
26944             }
26945         });
26946         for(var i = 0, len = this.buttons.length; i < len; i++){
26947             var btn = this.buttons[i];
26948             if(btn.formBind === true && btn.disabled === valid){
26949                 btn.setDisabled(!valid);
26950             }
26951         }
26952         this.fireEvent('clientvalidation', this, valid);
26953     }
26954     
26955     
26956     
26957     
26958     
26959     
26960     
26961     
26962 });
26963
26964
26965 // back compat
26966 Roo.Form = Roo.form.Form;
26967 /*
26968  * Based on:
26969  * Ext JS Library 1.1.1
26970  * Copyright(c) 2006-2007, Ext JS, LLC.
26971  *
26972  * Originally Released Under LGPL - original licence link has changed is not relivant.
26973  *
26974  * Fork - LGPL
26975  * <script type="text/javascript">
26976  */
26977  
26978  /**
26979  * @class Roo.form.Action
26980  * Internal Class used to handle form actions
26981  * @constructor
26982  * @param {Roo.form.BasicForm} el The form element or its id
26983  * @param {Object} config Configuration options
26984  */
26985  
26986  
26987 // define the action interface
26988 Roo.form.Action = function(form, options){
26989     this.form = form;
26990     this.options = options || {};
26991 };
26992 /**
26993  * Client Validation Failed
26994  * @const 
26995  */
26996 Roo.form.Action.CLIENT_INVALID = 'client';
26997 /**
26998  * Server Validation Failed
26999  * @const 
27000  */
27001  Roo.form.Action.SERVER_INVALID = 'server';
27002  /**
27003  * Connect to Server Failed
27004  * @const 
27005  */
27006 Roo.form.Action.CONNECT_FAILURE = 'connect';
27007 /**
27008  * Reading Data from Server Failed
27009  * @const 
27010  */
27011 Roo.form.Action.LOAD_FAILURE = 'load';
27012
27013 Roo.form.Action.prototype = {
27014     type : 'default',
27015     failureType : undefined,
27016     response : undefined,
27017     result : undefined,
27018
27019     // interface method
27020     run : function(options){
27021
27022     },
27023
27024     // interface method
27025     success : function(response){
27026
27027     },
27028
27029     // interface method
27030     handleResponse : function(response){
27031
27032     },
27033
27034     // default connection failure
27035     failure : function(response){
27036         
27037         this.response = response;
27038         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27039         this.form.afterAction(this, false);
27040     },
27041
27042     processResponse : function(response){
27043         this.response = response;
27044         if(!response.responseText){
27045             return true;
27046         }
27047         this.result = this.handleResponse(response);
27048         return this.result;
27049     },
27050
27051     // utility functions used internally
27052     getUrl : function(appendParams){
27053         var url = this.options.url || this.form.url || this.form.el.dom.action;
27054         if(appendParams){
27055             var p = this.getParams();
27056             if(p){
27057                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27058             }
27059         }
27060         return url;
27061     },
27062
27063     getMethod : function(){
27064         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27065     },
27066
27067     getParams : function(){
27068         var bp = this.form.baseParams;
27069         var p = this.options.params;
27070         if(p){
27071             if(typeof p == "object"){
27072                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27073             }else if(typeof p == 'string' && bp){
27074                 p += '&' + Roo.urlEncode(bp);
27075             }
27076         }else if(bp){
27077             p = Roo.urlEncode(bp);
27078         }
27079         return p;
27080     },
27081
27082     createCallback : function(){
27083         return {
27084             success: this.success,
27085             failure: this.failure,
27086             scope: this,
27087             timeout: (this.form.timeout*1000),
27088             upload: this.form.fileUpload ? this.success : undefined
27089         };
27090     }
27091 };
27092
27093 Roo.form.Action.Submit = function(form, options){
27094     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27095 };
27096
27097 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27098     type : 'submit',
27099
27100     haveProgress : false,
27101     uploadComplete : false,
27102     
27103     // uploadProgress indicator.
27104     uploadProgress : function()
27105     {
27106         if (!this.form.progressUrl) {
27107             return;
27108         }
27109         
27110         if (!this.haveProgress) {
27111             Roo.MessageBox.progress("Uploading", "Uploading");
27112         }
27113         if (this.uploadComplete) {
27114            Roo.MessageBox.hide();
27115            return;
27116         }
27117         
27118         this.haveProgress = true;
27119    
27120         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27121         
27122         var c = new Roo.data.Connection();
27123         c.request({
27124             url : this.form.progressUrl,
27125             params: {
27126                 id : uid
27127             },
27128             method: 'GET',
27129             success : function(req){
27130                //console.log(data);
27131                 var rdata = false;
27132                 var edata;
27133                 try  {
27134                    rdata = Roo.decode(req.responseText)
27135                 } catch (e) {
27136                     Roo.log("Invalid data from server..");
27137                     Roo.log(edata);
27138                     return;
27139                 }
27140                 if (!rdata || !rdata.success) {
27141                     Roo.log(rdata);
27142                     return;
27143                 }
27144                 var data = rdata.data;
27145                 
27146                 if (this.uploadComplete) {
27147                    Roo.MessageBox.hide();
27148                    return;
27149                 }
27150                    
27151                 if (data){
27152                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27153                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27154                     );
27155                 }
27156                 this.uploadProgress.defer(2000,this);
27157             },
27158        
27159             failure: function(data) {
27160                 Roo.log('progress url failed ');
27161                 Roo.log(data);
27162             },
27163             scope : this
27164         });
27165            
27166     },
27167     
27168     
27169     run : function()
27170     {
27171         // run get Values on the form, so it syncs any secondary forms.
27172         this.form.getValues();
27173         
27174         var o = this.options;
27175         var method = this.getMethod();
27176         var isPost = method == 'POST';
27177         if(o.clientValidation === false || this.form.isValid()){
27178             
27179             if (this.form.progressUrl) {
27180                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27181                     (new Date() * 1) + '' + Math.random());
27182                     
27183             } 
27184             
27185             
27186             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27187                 form:this.form.el.dom,
27188                 url:this.getUrl(!isPost),
27189                 method: method,
27190                 params:isPost ? this.getParams() : null,
27191                 isUpload: this.form.fileUpload
27192             }));
27193             
27194             this.uploadProgress();
27195
27196         }else if (o.clientValidation !== false){ // client validation failed
27197             this.failureType = Roo.form.Action.CLIENT_INVALID;
27198             this.form.afterAction(this, false);
27199         }
27200     },
27201
27202     success : function(response)
27203     {
27204         this.uploadComplete= true;
27205         if (this.haveProgress) {
27206             Roo.MessageBox.hide();
27207         }
27208         
27209         
27210         var result = this.processResponse(response);
27211         if(result === true || result.success){
27212             this.form.afterAction(this, true);
27213             return;
27214         }
27215         if(result.errors){
27216             this.form.markInvalid(result.errors);
27217             this.failureType = Roo.form.Action.SERVER_INVALID;
27218         }
27219         this.form.afterAction(this, false);
27220     },
27221     failure : function(response)
27222     {
27223         this.uploadComplete= true;
27224         if (this.haveProgress) {
27225             Roo.MessageBox.hide();
27226         }
27227         
27228         
27229         this.response = response;
27230         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27231         this.form.afterAction(this, false);
27232     },
27233     
27234     handleResponse : function(response){
27235         if(this.form.errorReader){
27236             var rs = this.form.errorReader.read(response);
27237             var errors = [];
27238             if(rs.records){
27239                 for(var i = 0, len = rs.records.length; i < len; i++) {
27240                     var r = rs.records[i];
27241                     errors[i] = r.data;
27242                 }
27243             }
27244             if(errors.length < 1){
27245                 errors = null;
27246             }
27247             return {
27248                 success : rs.success,
27249                 errors : errors
27250             };
27251         }
27252         var ret = false;
27253         try {
27254             ret = Roo.decode(response.responseText);
27255         } catch (e) {
27256             ret = {
27257                 success: false,
27258                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27259                 errors : []
27260             };
27261         }
27262         return ret;
27263         
27264     }
27265 });
27266
27267
27268 Roo.form.Action.Load = function(form, options){
27269     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27270     this.reader = this.form.reader;
27271 };
27272
27273 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27274     type : 'load',
27275
27276     run : function(){
27277         
27278         Roo.Ajax.request(Roo.apply(
27279                 this.createCallback(), {
27280                     method:this.getMethod(),
27281                     url:this.getUrl(false),
27282                     params:this.getParams()
27283         }));
27284     },
27285
27286     success : function(response){
27287         
27288         var result = this.processResponse(response);
27289         if(result === true || !result.success || !result.data){
27290             this.failureType = Roo.form.Action.LOAD_FAILURE;
27291             this.form.afterAction(this, false);
27292             return;
27293         }
27294         this.form.clearInvalid();
27295         this.form.setValues(result.data);
27296         this.form.afterAction(this, true);
27297     },
27298
27299     handleResponse : function(response){
27300         if(this.form.reader){
27301             var rs = this.form.reader.read(response);
27302             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27303             return {
27304                 success : rs.success,
27305                 data : data
27306             };
27307         }
27308         return Roo.decode(response.responseText);
27309     }
27310 });
27311
27312 Roo.form.Action.ACTION_TYPES = {
27313     'load' : Roo.form.Action.Load,
27314     'submit' : Roo.form.Action.Submit
27315 };/*
27316  * Based on:
27317  * Ext JS Library 1.1.1
27318  * Copyright(c) 2006-2007, Ext JS, LLC.
27319  *
27320  * Originally Released Under LGPL - original licence link has changed is not relivant.
27321  *
27322  * Fork - LGPL
27323  * <script type="text/javascript">
27324  */
27325  
27326 /**
27327  * @class Roo.form.Layout
27328  * @extends Roo.Component
27329  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27330  * @constructor
27331  * @param {Object} config Configuration options
27332  */
27333 Roo.form.Layout = function(config){
27334     var xitems = [];
27335     if (config.items) {
27336         xitems = config.items;
27337         delete config.items;
27338     }
27339     Roo.form.Layout.superclass.constructor.call(this, config);
27340     this.stack = [];
27341     Roo.each(xitems, this.addxtype, this);
27342      
27343 };
27344
27345 Roo.extend(Roo.form.Layout, Roo.Component, {
27346     /**
27347      * @cfg {String/Object} autoCreate
27348      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27349      */
27350     /**
27351      * @cfg {String/Object/Function} style
27352      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27353      * a function which returns such a specification.
27354      */
27355     /**
27356      * @cfg {String} labelAlign
27357      * Valid values are "left," "top" and "right" (defaults to "left")
27358      */
27359     /**
27360      * @cfg {Number} labelWidth
27361      * Fixed width in pixels of all field labels (defaults to undefined)
27362      */
27363     /**
27364      * @cfg {Boolean} clear
27365      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27366      */
27367     clear : true,
27368     /**
27369      * @cfg {String} labelSeparator
27370      * The separator to use after field labels (defaults to ':')
27371      */
27372     labelSeparator : ':',
27373     /**
27374      * @cfg {Boolean} hideLabels
27375      * True to suppress the display of field labels in this layout (defaults to false)
27376      */
27377     hideLabels : false,
27378
27379     // private
27380     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27381     
27382     isLayout : true,
27383     
27384     // private
27385     onRender : function(ct, position){
27386         if(this.el){ // from markup
27387             this.el = Roo.get(this.el);
27388         }else {  // generate
27389             var cfg = this.getAutoCreate();
27390             this.el = ct.createChild(cfg, position);
27391         }
27392         if(this.style){
27393             this.el.applyStyles(this.style);
27394         }
27395         if(this.labelAlign){
27396             this.el.addClass('x-form-label-'+this.labelAlign);
27397         }
27398         if(this.hideLabels){
27399             this.labelStyle = "display:none";
27400             this.elementStyle = "padding-left:0;";
27401         }else{
27402             if(typeof this.labelWidth == 'number'){
27403                 this.labelStyle = "width:"+this.labelWidth+"px;";
27404                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27405             }
27406             if(this.labelAlign == 'top'){
27407                 this.labelStyle = "width:auto;";
27408                 this.elementStyle = "padding-left:0;";
27409             }
27410         }
27411         var stack = this.stack;
27412         var slen = stack.length;
27413         if(slen > 0){
27414             if(!this.fieldTpl){
27415                 var t = new Roo.Template(
27416                     '<div class="x-form-item {5}">',
27417                         '<label for="{0}" style="{2}">{1}{4}</label>',
27418                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27419                         '</div>',
27420                     '</div><div class="x-form-clear-left"></div>'
27421                 );
27422                 t.disableFormats = true;
27423                 t.compile();
27424                 Roo.form.Layout.prototype.fieldTpl = t;
27425             }
27426             for(var i = 0; i < slen; i++) {
27427                 if(stack[i].isFormField){
27428                     this.renderField(stack[i]);
27429                 }else{
27430                     this.renderComponent(stack[i]);
27431                 }
27432             }
27433         }
27434         if(this.clear){
27435             this.el.createChild({cls:'x-form-clear'});
27436         }
27437     },
27438
27439     // private
27440     renderField : function(f){
27441         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27442                f.id, //0
27443                f.fieldLabel, //1
27444                f.labelStyle||this.labelStyle||'', //2
27445                this.elementStyle||'', //3
27446                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27447                f.itemCls||this.itemCls||''  //5
27448        ], true).getPrevSibling());
27449     },
27450
27451     // private
27452     renderComponent : function(c){
27453         c.render(c.isLayout ? this.el : this.el.createChild());    
27454     },
27455     /**
27456      * Adds a object form elements (using the xtype property as the factory method.)
27457      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27458      * @param {Object} config 
27459      */
27460     addxtype : function(o)
27461     {
27462         // create the lement.
27463         o.form = this.form;
27464         var fe = Roo.factory(o, Roo.form);
27465         this.form.allItems.push(fe);
27466         this.stack.push(fe);
27467         
27468         if (fe.isFormField) {
27469             this.form.items.add(fe);
27470         }
27471          
27472         return fe;
27473     }
27474 });
27475
27476 /**
27477  * @class Roo.form.Column
27478  * @extends Roo.form.Layout
27479  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27480  * @constructor
27481  * @param {Object} config Configuration options
27482  */
27483 Roo.form.Column = function(config){
27484     Roo.form.Column.superclass.constructor.call(this, config);
27485 };
27486
27487 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27488     /**
27489      * @cfg {Number/String} width
27490      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27491      */
27492     /**
27493      * @cfg {String/Object} autoCreate
27494      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27495      */
27496
27497     // private
27498     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27499
27500     // private
27501     onRender : function(ct, position){
27502         Roo.form.Column.superclass.onRender.call(this, ct, position);
27503         if(this.width){
27504             this.el.setWidth(this.width);
27505         }
27506     }
27507 });
27508
27509
27510 /**
27511  * @class Roo.form.Row
27512  * @extends Roo.form.Layout
27513  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27514  * @constructor
27515  * @param {Object} config Configuration options
27516  */
27517
27518  
27519 Roo.form.Row = function(config){
27520     Roo.form.Row.superclass.constructor.call(this, config);
27521 };
27522  
27523 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27524       /**
27525      * @cfg {Number/String} width
27526      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27527      */
27528     /**
27529      * @cfg {Number/String} height
27530      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27531      */
27532     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27533     
27534     padWidth : 20,
27535     // private
27536     onRender : function(ct, position){
27537         //console.log('row render');
27538         if(!this.rowTpl){
27539             var t = new Roo.Template(
27540                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27541                     '<label for="{0}" style="{2}">{1}{4}</label>',
27542                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27543                     '</div>',
27544                 '</div>'
27545             );
27546             t.disableFormats = true;
27547             t.compile();
27548             Roo.form.Layout.prototype.rowTpl = t;
27549         }
27550         this.fieldTpl = this.rowTpl;
27551         
27552         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27553         var labelWidth = 100;
27554         
27555         if ((this.labelAlign != 'top')) {
27556             if (typeof this.labelWidth == 'number') {
27557                 labelWidth = this.labelWidth
27558             }
27559             this.padWidth =  20 + labelWidth;
27560             
27561         }
27562         
27563         Roo.form.Column.superclass.onRender.call(this, ct, position);
27564         if(this.width){
27565             this.el.setWidth(this.width);
27566         }
27567         if(this.height){
27568             this.el.setHeight(this.height);
27569         }
27570     },
27571     
27572     // private
27573     renderField : function(f){
27574         f.fieldEl = this.fieldTpl.append(this.el, [
27575                f.id, f.fieldLabel,
27576                f.labelStyle||this.labelStyle||'',
27577                this.elementStyle||'',
27578                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27579                f.itemCls||this.itemCls||'',
27580                f.width ? f.width + this.padWidth : 160 + this.padWidth
27581        ],true);
27582     }
27583 });
27584  
27585
27586 /**
27587  * @class Roo.form.FieldSet
27588  * @extends Roo.form.Layout
27589  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27590  * @constructor
27591  * @param {Object} config Configuration options
27592  */
27593 Roo.form.FieldSet = function(config){
27594     Roo.form.FieldSet.superclass.constructor.call(this, config);
27595 };
27596
27597 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27598     /**
27599      * @cfg {String} legend
27600      * The text to display as the legend for the FieldSet (defaults to '')
27601      */
27602     /**
27603      * @cfg {String/Object} autoCreate
27604      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27605      */
27606
27607     // private
27608     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27609
27610     // private
27611     onRender : function(ct, position){
27612         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27613         if(this.legend){
27614             this.setLegend(this.legend);
27615         }
27616     },
27617
27618     // private
27619     setLegend : function(text){
27620         if(this.rendered){
27621             this.el.child('legend').update(text);
27622         }
27623     }
27624 });/*
27625  * Based on:
27626  * Ext JS Library 1.1.1
27627  * Copyright(c) 2006-2007, Ext JS, LLC.
27628  *
27629  * Originally Released Under LGPL - original licence link has changed is not relivant.
27630  *
27631  * Fork - LGPL
27632  * <script type="text/javascript">
27633  */
27634 /**
27635  * @class Roo.form.VTypes
27636  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27637  * @singleton
27638  */
27639 Roo.form.VTypes = function(){
27640     // closure these in so they are only created once.
27641     var alpha = /^[a-zA-Z_]+$/;
27642     var alphanum = /^[a-zA-Z0-9_]+$/;
27643     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27644     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27645
27646     // All these messages and functions are configurable
27647     return {
27648         /**
27649          * The function used to validate email addresses
27650          * @param {String} value The email address
27651          */
27652         'email' : function(v){
27653             return email.test(v);
27654         },
27655         /**
27656          * The error text to display when the email validation function returns false
27657          * @type String
27658          */
27659         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27660         /**
27661          * The keystroke filter mask to be applied on email input
27662          * @type RegExp
27663          */
27664         'emailMask' : /[a-z0-9_\.\-@]/i,
27665
27666         /**
27667          * The function used to validate URLs
27668          * @param {String} value The URL
27669          */
27670         'url' : function(v){
27671             return url.test(v);
27672         },
27673         /**
27674          * The error text to display when the url validation function returns false
27675          * @type String
27676          */
27677         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27678         
27679         /**
27680          * The function used to validate alpha values
27681          * @param {String} value The value
27682          */
27683         'alpha' : function(v){
27684             return alpha.test(v);
27685         },
27686         /**
27687          * The error text to display when the alpha validation function returns false
27688          * @type String
27689          */
27690         'alphaText' : 'This field should only contain letters and _',
27691         /**
27692          * The keystroke filter mask to be applied on alpha input
27693          * @type RegExp
27694          */
27695         'alphaMask' : /[a-z_]/i,
27696
27697         /**
27698          * The function used to validate alphanumeric values
27699          * @param {String} value The value
27700          */
27701         'alphanum' : function(v){
27702             return alphanum.test(v);
27703         },
27704         /**
27705          * The error text to display when the alphanumeric validation function returns false
27706          * @type String
27707          */
27708         'alphanumText' : 'This field should only contain letters, numbers and _',
27709         /**
27710          * The keystroke filter mask to be applied on alphanumeric input
27711          * @type RegExp
27712          */
27713         'alphanumMask' : /[a-z0-9_]/i
27714     };
27715 }();//<script type="text/javascript">
27716
27717 /**
27718  * @class Roo.form.FCKeditor
27719  * @extends Roo.form.TextArea
27720  * Wrapper around the FCKEditor http://www.fckeditor.net
27721  * @constructor
27722  * Creates a new FCKeditor
27723  * @param {Object} config Configuration options
27724  */
27725 Roo.form.FCKeditor = function(config){
27726     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27727     this.addEvents({
27728          /**
27729          * @event editorinit
27730          * Fired when the editor is initialized - you can add extra handlers here..
27731          * @param {FCKeditor} this
27732          * @param {Object} the FCK object.
27733          */
27734         editorinit : true
27735     });
27736     
27737     
27738 };
27739 Roo.form.FCKeditor.editors = { };
27740 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27741 {
27742     //defaultAutoCreate : {
27743     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27744     //},
27745     // private
27746     /**
27747      * @cfg {Object} fck options - see fck manual for details.
27748      */
27749     fckconfig : false,
27750     
27751     /**
27752      * @cfg {Object} fck toolbar set (Basic or Default)
27753      */
27754     toolbarSet : 'Basic',
27755     /**
27756      * @cfg {Object} fck BasePath
27757      */ 
27758     basePath : '/fckeditor/',
27759     
27760     
27761     frame : false,
27762     
27763     value : '',
27764     
27765    
27766     onRender : function(ct, position)
27767     {
27768         if(!this.el){
27769             this.defaultAutoCreate = {
27770                 tag: "textarea",
27771                 style:"width:300px;height:60px;",
27772                 autocomplete: "off"
27773             };
27774         }
27775         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27776         /*
27777         if(this.grow){
27778             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27779             if(this.preventScrollbars){
27780                 this.el.setStyle("overflow", "hidden");
27781             }
27782             this.el.setHeight(this.growMin);
27783         }
27784         */
27785         //console.log('onrender' + this.getId() );
27786         Roo.form.FCKeditor.editors[this.getId()] = this;
27787          
27788
27789         this.replaceTextarea() ;
27790         
27791     },
27792     
27793     getEditor : function() {
27794         return this.fckEditor;
27795     },
27796     /**
27797      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27798      * @param {Mixed} value The value to set
27799      */
27800     
27801     
27802     setValue : function(value)
27803     {
27804         //console.log('setValue: ' + value);
27805         
27806         if(typeof(value) == 'undefined') { // not sure why this is happending...
27807             return;
27808         }
27809         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27810         
27811         //if(!this.el || !this.getEditor()) {
27812         //    this.value = value;
27813             //this.setValue.defer(100,this,[value]);    
27814         //    return;
27815         //} 
27816         
27817         if(!this.getEditor()) {
27818             return;
27819         }
27820         
27821         this.getEditor().SetData(value);
27822         
27823         //
27824
27825     },
27826
27827     /**
27828      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27829      * @return {Mixed} value The field value
27830      */
27831     getValue : function()
27832     {
27833         
27834         if (this.frame && this.frame.dom.style.display == 'none') {
27835             return Roo.form.FCKeditor.superclass.getValue.call(this);
27836         }
27837         
27838         if(!this.el || !this.getEditor()) {
27839            
27840            // this.getValue.defer(100,this); 
27841             return this.value;
27842         }
27843        
27844         
27845         var value=this.getEditor().GetData();
27846         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27847         return Roo.form.FCKeditor.superclass.getValue.call(this);
27848         
27849
27850     },
27851
27852     /**
27853      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27854      * @return {Mixed} value The field value
27855      */
27856     getRawValue : function()
27857     {
27858         if (this.frame && this.frame.dom.style.display == 'none') {
27859             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27860         }
27861         
27862         if(!this.el || !this.getEditor()) {
27863             //this.getRawValue.defer(100,this); 
27864             return this.value;
27865             return;
27866         }
27867         
27868         
27869         
27870         var value=this.getEditor().GetData();
27871         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27872         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27873          
27874     },
27875     
27876     setSize : function(w,h) {
27877         
27878         
27879         
27880         //if (this.frame && this.frame.dom.style.display == 'none') {
27881         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27882         //    return;
27883         //}
27884         //if(!this.el || !this.getEditor()) {
27885         //    this.setSize.defer(100,this, [w,h]); 
27886         //    return;
27887         //}
27888         
27889         
27890         
27891         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27892         
27893         this.frame.dom.setAttribute('width', w);
27894         this.frame.dom.setAttribute('height', h);
27895         this.frame.setSize(w,h);
27896         
27897     },
27898     
27899     toggleSourceEdit : function(value) {
27900         
27901       
27902          
27903         this.el.dom.style.display = value ? '' : 'none';
27904         this.frame.dom.style.display = value ?  'none' : '';
27905         
27906     },
27907     
27908     
27909     focus: function(tag)
27910     {
27911         if (this.frame.dom.style.display == 'none') {
27912             return Roo.form.FCKeditor.superclass.focus.call(this);
27913         }
27914         if(!this.el || !this.getEditor()) {
27915             this.focus.defer(100,this, [tag]); 
27916             return;
27917         }
27918         
27919         
27920         
27921         
27922         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27923         this.getEditor().Focus();
27924         if (tgs.length) {
27925             if (!this.getEditor().Selection.GetSelection()) {
27926                 this.focus.defer(100,this, [tag]); 
27927                 return;
27928             }
27929             
27930             
27931             var r = this.getEditor().EditorDocument.createRange();
27932             r.setStart(tgs[0],0);
27933             r.setEnd(tgs[0],0);
27934             this.getEditor().Selection.GetSelection().removeAllRanges();
27935             this.getEditor().Selection.GetSelection().addRange(r);
27936             this.getEditor().Focus();
27937         }
27938         
27939     },
27940     
27941     
27942     
27943     replaceTextarea : function()
27944     {
27945         if ( document.getElementById( this.getId() + '___Frame' ) )
27946             return ;
27947         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27948         //{
27949             // We must check the elements firstly using the Id and then the name.
27950         var oTextarea = document.getElementById( this.getId() );
27951         
27952         var colElementsByName = document.getElementsByName( this.getId() ) ;
27953          
27954         oTextarea.style.display = 'none' ;
27955
27956         if ( oTextarea.tabIndex ) {            
27957             this.TabIndex = oTextarea.tabIndex ;
27958         }
27959         
27960         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27961         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27962         this.frame = Roo.get(this.getId() + '___Frame')
27963     },
27964     
27965     _getConfigHtml : function()
27966     {
27967         var sConfig = '' ;
27968
27969         for ( var o in this.fckconfig ) {
27970             sConfig += sConfig.length > 0  ? '&amp;' : '';
27971             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27972         }
27973
27974         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27975     },
27976     
27977     
27978     _getIFrameHtml : function()
27979     {
27980         var sFile = 'fckeditor.html' ;
27981         /* no idea what this is about..
27982         try
27983         {
27984             if ( (/fcksource=true/i).test( window.top.location.search ) )
27985                 sFile = 'fckeditor.original.html' ;
27986         }
27987         catch (e) { 
27988         */
27989
27990         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27991         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27992         
27993         
27994         var html = '<iframe id="' + this.getId() +
27995             '___Frame" src="' + sLink +
27996             '" width="' + this.width +
27997             '" height="' + this.height + '"' +
27998             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27999             ' frameborder="0" scrolling="no"></iframe>' ;
28000
28001         return html ;
28002     },
28003     
28004     _insertHtmlBefore : function( html, element )
28005     {
28006         if ( element.insertAdjacentHTML )       {
28007             // IE
28008             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28009         } else { // Gecko
28010             var oRange = document.createRange() ;
28011             oRange.setStartBefore( element ) ;
28012             var oFragment = oRange.createContextualFragment( html );
28013             element.parentNode.insertBefore( oFragment, element ) ;
28014         }
28015     }
28016     
28017     
28018   
28019     
28020     
28021     
28022     
28023
28024 });
28025
28026 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28027
28028 function FCKeditor_OnComplete(editorInstance){
28029     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28030     f.fckEditor = editorInstance;
28031     //console.log("loaded");
28032     f.fireEvent('editorinit', f, editorInstance);
28033
28034   
28035
28036  
28037
28038
28039
28040
28041
28042
28043
28044
28045
28046
28047
28048
28049
28050
28051
28052 //<script type="text/javascript">
28053 /**
28054  * @class Roo.form.GridField
28055  * @extends Roo.form.Field
28056  * Embed a grid (or editable grid into a form)
28057  * STATUS ALPHA
28058  * 
28059  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28060  * it needs 
28061  * xgrid.store = Roo.data.Store
28062  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28063  * xgrid.store.reader = Roo.data.JsonReader 
28064  * 
28065  * 
28066  * @constructor
28067  * Creates a new GridField
28068  * @param {Object} config Configuration options
28069  */
28070 Roo.form.GridField = function(config){
28071     Roo.form.GridField.superclass.constructor.call(this, config);
28072      
28073 };
28074
28075 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28076     /**
28077      * @cfg {Number} width  - used to restrict width of grid..
28078      */
28079     width : 100,
28080     /**
28081      * @cfg {Number} height - used to restrict height of grid..
28082      */
28083     height : 50,
28084      /**
28085      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28086          * 
28087          *}
28088      */
28089     xgrid : false, 
28090     /**
28091      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28092      * {tag: "input", type: "checkbox", autocomplete: "off"})
28093      */
28094    // defaultAutoCreate : { tag: 'div' },
28095     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28096     /**
28097      * @cfg {String} addTitle Text to include for adding a title.
28098      */
28099     addTitle : false,
28100     //
28101     onResize : function(){
28102         Roo.form.Field.superclass.onResize.apply(this, arguments);
28103     },
28104
28105     initEvents : function(){
28106         // Roo.form.Checkbox.superclass.initEvents.call(this);
28107         // has no events...
28108        
28109     },
28110
28111
28112     getResizeEl : function(){
28113         return this.wrap;
28114     },
28115
28116     getPositionEl : function(){
28117         return this.wrap;
28118     },
28119
28120     // private
28121     onRender : function(ct, position){
28122         
28123         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28124         var style = this.style;
28125         delete this.style;
28126         
28127         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28128         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28129         this.viewEl = this.wrap.createChild({ tag: 'div' });
28130         if (style) {
28131             this.viewEl.applyStyles(style);
28132         }
28133         if (this.width) {
28134             this.viewEl.setWidth(this.width);
28135         }
28136         if (this.height) {
28137             this.viewEl.setHeight(this.height);
28138         }
28139         //if(this.inputValue !== undefined){
28140         //this.setValue(this.value);
28141         
28142         
28143         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28144         
28145         
28146         this.grid.render();
28147         this.grid.getDataSource().on('remove', this.refreshValue, this);
28148         this.grid.getDataSource().on('update', this.refreshValue, this);
28149         this.grid.on('afteredit', this.refreshValue, this);
28150  
28151     },
28152      
28153     
28154     /**
28155      * Sets the value of the item. 
28156      * @param {String} either an object  or a string..
28157      */
28158     setValue : function(v){
28159         //this.value = v;
28160         v = v || []; // empty set..
28161         // this does not seem smart - it really only affects memoryproxy grids..
28162         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28163             var ds = this.grid.getDataSource();
28164             // assumes a json reader..
28165             var data = {}
28166             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28167             ds.loadData( data);
28168         }
28169         Roo.form.GridField.superclass.setValue.call(this, v);
28170         this.refreshValue();
28171         // should load data in the grid really....
28172     },
28173     
28174     // private
28175     refreshValue: function() {
28176          var val = [];
28177         this.grid.getDataSource().each(function(r) {
28178             val.push(r.data);
28179         });
28180         this.el.dom.value = Roo.encode(val);
28181     }
28182     
28183      
28184     
28185     
28186 });/*
28187  * Based on:
28188  * Ext JS Library 1.1.1
28189  * Copyright(c) 2006-2007, Ext JS, LLC.
28190  *
28191  * Originally Released Under LGPL - original licence link has changed is not relivant.
28192  *
28193  * Fork - LGPL
28194  * <script type="text/javascript">
28195  */
28196 /**
28197  * @class Roo.form.DisplayField
28198  * @extends Roo.form.Field
28199  * A generic Field to display non-editable data.
28200  * @constructor
28201  * Creates a new Display Field item.
28202  * @param {Object} config Configuration options
28203  */
28204 Roo.form.DisplayField = function(config){
28205     Roo.form.DisplayField.superclass.constructor.call(this, config);
28206     
28207 };
28208
28209 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28210     inputType:      'hidden',
28211     allowBlank:     true,
28212     readOnly:         true,
28213     
28214  
28215     /**
28216      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28217      */
28218     focusClass : undefined,
28219     /**
28220      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28221      */
28222     fieldClass: 'x-form-field',
28223     
28224      /**
28225      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28226      */
28227     valueRenderer: undefined,
28228     
28229     width: 100,
28230     /**
28231      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28232      * {tag: "input", type: "checkbox", autocomplete: "off"})
28233      */
28234      
28235  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28236
28237     onResize : function(){
28238         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28239         
28240     },
28241
28242     initEvents : function(){
28243         // Roo.form.Checkbox.superclass.initEvents.call(this);
28244         // has no events...
28245        
28246     },
28247
28248
28249     getResizeEl : function(){
28250         return this.wrap;
28251     },
28252
28253     getPositionEl : function(){
28254         return this.wrap;
28255     },
28256
28257     // private
28258     onRender : function(ct, position){
28259         
28260         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28261         //if(this.inputValue !== undefined){
28262         this.wrap = this.el.wrap();
28263         
28264         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28265         
28266         if (this.bodyStyle) {
28267             this.viewEl.applyStyles(this.bodyStyle);
28268         }
28269         //this.viewEl.setStyle('padding', '2px');
28270         
28271         this.setValue(this.value);
28272         
28273     },
28274 /*
28275     // private
28276     initValue : Roo.emptyFn,
28277
28278   */
28279
28280         // private
28281     onClick : function(){
28282         
28283     },
28284
28285     /**
28286      * Sets the checked state of the checkbox.
28287      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28288      */
28289     setValue : function(v){
28290         this.value = v;
28291         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28292         // this might be called before we have a dom element..
28293         if (!this.viewEl) {
28294             return;
28295         }
28296         this.viewEl.dom.innerHTML = html;
28297         Roo.form.DisplayField.superclass.setValue.call(this, v);
28298
28299     }
28300 });/*
28301  * 
28302  * Licence- LGPL
28303  * 
28304  */
28305
28306 /**
28307  * @class Roo.form.DayPicker
28308  * @extends Roo.form.Field
28309  * A Day picker show [M] [T] [W] ....
28310  * @constructor
28311  * Creates a new Day Picker
28312  * @param {Object} config Configuration options
28313  */
28314 Roo.form.DayPicker= function(config){
28315     Roo.form.DayPicker.superclass.constructor.call(this, config);
28316      
28317 };
28318
28319 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28320     /**
28321      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28322      */
28323     focusClass : undefined,
28324     /**
28325      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28326      */
28327     fieldClass: "x-form-field",
28328    
28329     /**
28330      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28331      * {tag: "input", type: "checkbox", autocomplete: "off"})
28332      */
28333     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28334     
28335    
28336     actionMode : 'viewEl', 
28337     //
28338     // private
28339  
28340     inputType : 'hidden',
28341     
28342      
28343     inputElement: false, // real input element?
28344     basedOn: false, // ????
28345     
28346     isFormField: true, // not sure where this is needed!!!!
28347
28348     onResize : function(){
28349         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28350         if(!this.boxLabel){
28351             this.el.alignTo(this.wrap, 'c-c');
28352         }
28353     },
28354
28355     initEvents : function(){
28356         Roo.form.Checkbox.superclass.initEvents.call(this);
28357         this.el.on("click", this.onClick,  this);
28358         this.el.on("change", this.onClick,  this);
28359     },
28360
28361
28362     getResizeEl : function(){
28363         return this.wrap;
28364     },
28365
28366     getPositionEl : function(){
28367         return this.wrap;
28368     },
28369
28370     
28371     // private
28372     onRender : function(ct, position){
28373         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28374        
28375         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28376         
28377         var r1 = '<table><tr>';
28378         var r2 = '<tr class="x-form-daypick-icons">';
28379         for (var i=0; i < 7; i++) {
28380             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28381             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28382         }
28383         
28384         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28385         viewEl.select('img').on('click', this.onClick, this);
28386         this.viewEl = viewEl;   
28387         
28388         
28389         // this will not work on Chrome!!!
28390         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28391         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28392         
28393         
28394           
28395
28396     },
28397
28398     // private
28399     initValue : Roo.emptyFn,
28400
28401     /**
28402      * Returns the checked state of the checkbox.
28403      * @return {Boolean} True if checked, else false
28404      */
28405     getValue : function(){
28406         return this.el.dom.value;
28407         
28408     },
28409
28410         // private
28411     onClick : function(e){ 
28412         //this.setChecked(!this.checked);
28413         Roo.get(e.target).toggleClass('x-menu-item-checked');
28414         this.refreshValue();
28415         //if(this.el.dom.checked != this.checked){
28416         //    this.setValue(this.el.dom.checked);
28417        // }
28418     },
28419     
28420     // private
28421     refreshValue : function()
28422     {
28423         var val = '';
28424         this.viewEl.select('img',true).each(function(e,i,n)  {
28425             val += e.is(".x-menu-item-checked") ? String(n) : '';
28426         });
28427         this.setValue(val, true);
28428     },
28429
28430     /**
28431      * Sets the checked state of the checkbox.
28432      * On is always based on a string comparison between inputValue and the param.
28433      * @param {Boolean/String} value - the value to set 
28434      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28435      */
28436     setValue : function(v,suppressEvent){
28437         if (!this.el.dom) {
28438             return;
28439         }
28440         var old = this.el.dom.value ;
28441         this.el.dom.value = v;
28442         if (suppressEvent) {
28443             return ;
28444         }
28445          
28446         // update display..
28447         this.viewEl.select('img',true).each(function(e,i,n)  {
28448             
28449             var on = e.is(".x-menu-item-checked");
28450             var newv = v.indexOf(String(n)) > -1;
28451             if (on != newv) {
28452                 e.toggleClass('x-menu-item-checked');
28453             }
28454             
28455         });
28456         
28457         
28458         this.fireEvent('change', this, v, old);
28459         
28460         
28461     },
28462    
28463     // handle setting of hidden value by some other method!!?!?
28464     setFromHidden: function()
28465     {
28466         if(!this.el){
28467             return;
28468         }
28469         //console.log("SET FROM HIDDEN");
28470         //alert('setFrom hidden');
28471         this.setValue(this.el.dom.value);
28472     },
28473     
28474     onDestroy : function()
28475     {
28476         if(this.viewEl){
28477             Roo.get(this.viewEl).remove();
28478         }
28479          
28480         Roo.form.DayPicker.superclass.onDestroy.call(this);
28481     }
28482
28483 });//<script type="text/javasscript">
28484  
28485
28486 /**
28487  * @class Roo.DDView
28488  * A DnD enabled version of Roo.View.
28489  * @param {Element/String} container The Element in which to create the View.
28490  * @param {String} tpl The template string used to create the markup for each element of the View
28491  * @param {Object} config The configuration properties. These include all the config options of
28492  * {@link Roo.View} plus some specific to this class.<br>
28493  * <p>
28494  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28495  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28496  * <p>
28497  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28498 .x-view-drag-insert-above {
28499         border-top:1px dotted #3366cc;
28500 }
28501 .x-view-drag-insert-below {
28502         border-bottom:1px dotted #3366cc;
28503 }
28504 </code></pre>
28505  * 
28506  */
28507  
28508 Roo.DDView = function(container, tpl, config) {
28509     Roo.DDView.superclass.constructor.apply(this, arguments);
28510     this.getEl().setStyle("outline", "0px none");
28511     this.getEl().unselectable();
28512     if (this.dragGroup) {
28513                 this.setDraggable(this.dragGroup.split(","));
28514     }
28515     if (this.dropGroup) {
28516                 this.setDroppable(this.dropGroup.split(","));
28517     }
28518     if (this.deletable) {
28519         this.setDeletable();
28520     }
28521     this.isDirtyFlag = false;
28522         this.addEvents({
28523                 "drop" : true
28524         });
28525 };
28526
28527 Roo.extend(Roo.DDView, Roo.View, {
28528 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28529 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28530 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28531 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28532
28533         isFormField: true,
28534
28535         reset: Roo.emptyFn,
28536         
28537         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28538
28539         validate: function() {
28540                 return true;
28541         },
28542         
28543         destroy: function() {
28544                 this.purgeListeners();
28545                 this.getEl.removeAllListeners();
28546                 this.getEl().remove();
28547                 if (this.dragZone) {
28548                         if (this.dragZone.destroy) {
28549                                 this.dragZone.destroy();
28550                         }
28551                 }
28552                 if (this.dropZone) {
28553                         if (this.dropZone.destroy) {
28554                                 this.dropZone.destroy();
28555                         }
28556                 }
28557         },
28558
28559 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28560         getName: function() {
28561                 return this.name;
28562         },
28563
28564 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28565         setValue: function(v) {
28566                 if (!this.store) {
28567                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28568                 }
28569                 var data = {};
28570                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28571                 this.store.proxy = new Roo.data.MemoryProxy(data);
28572                 this.store.load();
28573         },
28574
28575 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28576         getValue: function() {
28577                 var result = '(';
28578                 this.store.each(function(rec) {
28579                         result += rec.id + ',';
28580                 });
28581                 return result.substr(0, result.length - 1) + ')';
28582         },
28583         
28584         getIds: function() {
28585                 var i = 0, result = new Array(this.store.getCount());
28586                 this.store.each(function(rec) {
28587                         result[i++] = rec.id;
28588                 });
28589                 return result;
28590         },
28591         
28592         isDirty: function() {
28593                 return this.isDirtyFlag;
28594         },
28595
28596 /**
28597  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28598  *      whole Element becomes the target, and this causes the drop gesture to append.
28599  */
28600     getTargetFromEvent : function(e) {
28601                 var target = e.getTarget();
28602                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28603                 target = target.parentNode;
28604                 }
28605                 if (!target) {
28606                         target = this.el.dom.lastChild || this.el.dom;
28607                 }
28608                 return target;
28609     },
28610
28611 /**
28612  *      Create the drag data which consists of an object which has the property "ddel" as
28613  *      the drag proxy element. 
28614  */
28615     getDragData : function(e) {
28616         var target = this.findItemFromChild(e.getTarget());
28617                 if(target) {
28618                         this.handleSelection(e);
28619                         var selNodes = this.getSelectedNodes();
28620             var dragData = {
28621                 source: this,
28622                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28623                 nodes: selNodes,
28624                 records: []
28625                         };
28626                         var selectedIndices = this.getSelectedIndexes();
28627                         for (var i = 0; i < selectedIndices.length; i++) {
28628                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28629                         }
28630                         if (selNodes.length == 1) {
28631                                 dragData.ddel = target.cloneNode(true); // the div element
28632                         } else {
28633                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28634                                 div.className = 'multi-proxy';
28635                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28636                                         div.appendChild(selNodes[i].cloneNode(true));
28637                                 }
28638                                 dragData.ddel = div;
28639                         }
28640             //console.log(dragData)
28641             //console.log(dragData.ddel.innerHTML)
28642                         return dragData;
28643                 }
28644         //console.log('nodragData')
28645                 return false;
28646     },
28647     
28648 /**     Specify to which ddGroup items in this DDView may be dragged. */
28649     setDraggable: function(ddGroup) {
28650         if (ddGroup instanceof Array) {
28651                 Roo.each(ddGroup, this.setDraggable, this);
28652                 return;
28653         }
28654         if (this.dragZone) {
28655                 this.dragZone.addToGroup(ddGroup);
28656         } else {
28657                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28658                                 containerScroll: true,
28659                                 ddGroup: ddGroup 
28660
28661                         });
28662 //                      Draggability implies selection. DragZone's mousedown selects the element.
28663                         if (!this.multiSelect) { this.singleSelect = true; }
28664
28665 //                      Wire the DragZone's handlers up to methods in *this*
28666                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28667                 }
28668     },
28669
28670 /**     Specify from which ddGroup this DDView accepts drops. */
28671     setDroppable: function(ddGroup) {
28672         if (ddGroup instanceof Array) {
28673                 Roo.each(ddGroup, this.setDroppable, this);
28674                 return;
28675         }
28676         if (this.dropZone) {
28677                 this.dropZone.addToGroup(ddGroup);
28678         } else {
28679                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28680                                 containerScroll: true,
28681                                 ddGroup: ddGroup
28682                         });
28683
28684 //                      Wire the DropZone's handlers up to methods in *this*
28685                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28686                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28687                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28688                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28689                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28690                 }
28691     },
28692
28693 /**     Decide whether to drop above or below a View node. */
28694     getDropPoint : function(e, n, dd){
28695         if (n == this.el.dom) { return "above"; }
28696                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28697                 var c = t + (b - t) / 2;
28698                 var y = Roo.lib.Event.getPageY(e);
28699                 if(y <= c) {
28700                         return "above";
28701                 }else{
28702                         return "below";
28703                 }
28704     },
28705
28706     onNodeEnter : function(n, dd, e, data){
28707                 return false;
28708     },
28709     
28710     onNodeOver : function(n, dd, e, data){
28711                 var pt = this.getDropPoint(e, n, dd);
28712                 // set the insert point style on the target node
28713                 var dragElClass = this.dropNotAllowed;
28714                 if (pt) {
28715                         var targetElClass;
28716                         if (pt == "above"){
28717                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28718                                 targetElClass = "x-view-drag-insert-above";
28719                         } else {
28720                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28721                                 targetElClass = "x-view-drag-insert-below";
28722                         }
28723                         if (this.lastInsertClass != targetElClass){
28724                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28725                                 this.lastInsertClass = targetElClass;
28726                         }
28727                 }
28728                 return dragElClass;
28729         },
28730
28731     onNodeOut : function(n, dd, e, data){
28732                 this.removeDropIndicators(n);
28733     },
28734
28735     onNodeDrop : function(n, dd, e, data){
28736         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28737                 return false;
28738         }
28739         var pt = this.getDropPoint(e, n, dd);
28740                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28741                 if (pt == "below") { insertAt++; }
28742                 for (var i = 0; i < data.records.length; i++) {
28743                         var r = data.records[i];
28744                         var dup = this.store.getById(r.id);
28745                         if (dup && (dd != this.dragZone)) {
28746                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28747                         } else {
28748                                 if (data.copy) {
28749                                         this.store.insert(insertAt++, r.copy());
28750                                 } else {
28751                                         data.source.isDirtyFlag = true;
28752                                         r.store.remove(r);
28753                                         this.store.insert(insertAt++, r);
28754                                 }
28755                                 this.isDirtyFlag = true;
28756                         }
28757                 }
28758                 this.dragZone.cachedTarget = null;
28759                 return true;
28760     },
28761
28762     removeDropIndicators : function(n){
28763                 if(n){
28764                         Roo.fly(n).removeClass([
28765                                 "x-view-drag-insert-above",
28766                                 "x-view-drag-insert-below"]);
28767                         this.lastInsertClass = "_noclass";
28768                 }
28769     },
28770
28771 /**
28772  *      Utility method. Add a delete option to the DDView's context menu.
28773  *      @param {String} imageUrl The URL of the "delete" icon image.
28774  */
28775         setDeletable: function(imageUrl) {
28776                 if (!this.singleSelect && !this.multiSelect) {
28777                         this.singleSelect = true;
28778                 }
28779                 var c = this.getContextMenu();
28780                 this.contextMenu.on("itemclick", function(item) {
28781                         switch (item.id) {
28782                                 case "delete":
28783                                         this.remove(this.getSelectedIndexes());
28784                                         break;
28785                         }
28786                 }, this);
28787                 this.contextMenu.add({
28788                         icon: imageUrl,
28789                         id: "delete",
28790                         text: 'Delete'
28791                 });
28792         },
28793         
28794 /**     Return the context menu for this DDView. */
28795         getContextMenu: function() {
28796                 if (!this.contextMenu) {
28797 //                      Create the View's context menu
28798                         this.contextMenu = new Roo.menu.Menu({
28799                                 id: this.id + "-contextmenu"
28800                         });
28801                         this.el.on("contextmenu", this.showContextMenu, this);
28802                 }
28803                 return this.contextMenu;
28804         },
28805         
28806         disableContextMenu: function() {
28807                 if (this.contextMenu) {
28808                         this.el.un("contextmenu", this.showContextMenu, this);
28809                 }
28810         },
28811
28812         showContextMenu: function(e, item) {
28813         item = this.findItemFromChild(e.getTarget());
28814                 if (item) {
28815                         e.stopEvent();
28816                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28817                         this.contextMenu.showAt(e.getXY());
28818             }
28819     },
28820
28821 /**
28822  *      Remove {@link Roo.data.Record}s at the specified indices.
28823  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28824  */
28825     remove: function(selectedIndices) {
28826                 selectedIndices = [].concat(selectedIndices);
28827                 for (var i = 0; i < selectedIndices.length; i++) {
28828                         var rec = this.store.getAt(selectedIndices[i]);
28829                         this.store.remove(rec);
28830                 }
28831     },
28832
28833 /**
28834  *      Double click fires the event, but also, if this is draggable, and there is only one other
28835  *      related DropZone, it transfers the selected node.
28836  */
28837     onDblClick : function(e){
28838         var item = this.findItemFromChild(e.getTarget());
28839         if(item){
28840             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28841                 return false;
28842             }
28843             if (this.dragGroup) {
28844                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28845                     while (targets.indexOf(this.dropZone) > -1) {
28846                             targets.remove(this.dropZone);
28847                                 }
28848                     if (targets.length == 1) {
28849                                         this.dragZone.cachedTarget = null;
28850                         var el = Roo.get(targets[0].getEl());
28851                         var box = el.getBox(true);
28852                         targets[0].onNodeDrop(el.dom, {
28853                                 target: el.dom,
28854                                 xy: [box.x, box.y + box.height - 1]
28855                         }, null, this.getDragData(e));
28856                     }
28857                 }
28858         }
28859     },
28860     
28861     handleSelection: function(e) {
28862                 this.dragZone.cachedTarget = null;
28863         var item = this.findItemFromChild(e.getTarget());
28864         if (!item) {
28865                 this.clearSelections(true);
28866                 return;
28867         }
28868                 if (item && (this.multiSelect || this.singleSelect)){
28869                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28870                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28871                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28872                                 this.unselect(item);
28873                         } else {
28874                                 this.select(item, this.multiSelect && e.ctrlKey);
28875                                 this.lastSelection = item;
28876                         }
28877                 }
28878     },
28879
28880     onItemClick : function(item, index, e){
28881                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28882                         return false;
28883                 }
28884                 return true;
28885     },
28886
28887     unselect : function(nodeInfo, suppressEvent){
28888                 var node = this.getNode(nodeInfo);
28889                 if(node && this.isSelected(node)){
28890                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28891                                 Roo.fly(node).removeClass(this.selectedClass);
28892                                 this.selections.remove(node);
28893                                 if(!suppressEvent){
28894                                         this.fireEvent("selectionchange", this, this.selections);
28895                                 }
28896                         }
28897                 }
28898     }
28899 });
28900 /*
28901  * Based on:
28902  * Ext JS Library 1.1.1
28903  * Copyright(c) 2006-2007, Ext JS, LLC.
28904  *
28905  * Originally Released Under LGPL - original licence link has changed is not relivant.
28906  *
28907  * Fork - LGPL
28908  * <script type="text/javascript">
28909  */
28910  
28911 /**
28912  * @class Roo.LayoutManager
28913  * @extends Roo.util.Observable
28914  * Base class for layout managers.
28915  */
28916 Roo.LayoutManager = function(container, config){
28917     Roo.LayoutManager.superclass.constructor.call(this);
28918     this.el = Roo.get(container);
28919     // ie scrollbar fix
28920     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28921         document.body.scroll = "no";
28922     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28923         this.el.position('relative');
28924     }
28925     this.id = this.el.id;
28926     this.el.addClass("x-layout-container");
28927     /** false to disable window resize monitoring @type Boolean */
28928     this.monitorWindowResize = true;
28929     this.regions = {};
28930     this.addEvents({
28931         /**
28932          * @event layout
28933          * Fires when a layout is performed. 
28934          * @param {Roo.LayoutManager} this
28935          */
28936         "layout" : true,
28937         /**
28938          * @event regionresized
28939          * Fires when the user resizes a region. 
28940          * @param {Roo.LayoutRegion} region The resized region
28941          * @param {Number} newSize The new size (width for east/west, height for north/south)
28942          */
28943         "regionresized" : true,
28944         /**
28945          * @event regioncollapsed
28946          * Fires when a region is collapsed. 
28947          * @param {Roo.LayoutRegion} region The collapsed region
28948          */
28949         "regioncollapsed" : true,
28950         /**
28951          * @event regionexpanded
28952          * Fires when a region is expanded.  
28953          * @param {Roo.LayoutRegion} region The expanded region
28954          */
28955         "regionexpanded" : true
28956     });
28957     this.updating = false;
28958     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28959 };
28960
28961 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28962     /**
28963      * Returns true if this layout is currently being updated
28964      * @return {Boolean}
28965      */
28966     isUpdating : function(){
28967         return this.updating; 
28968     },
28969     
28970     /**
28971      * Suspend the LayoutManager from doing auto-layouts while
28972      * making multiple add or remove calls
28973      */
28974     beginUpdate : function(){
28975         this.updating = true;    
28976     },
28977     
28978     /**
28979      * Restore auto-layouts and optionally disable the manager from performing a layout
28980      * @param {Boolean} noLayout true to disable a layout update 
28981      */
28982     endUpdate : function(noLayout){
28983         this.updating = false;
28984         if(!noLayout){
28985             this.layout();
28986         }    
28987     },
28988     
28989     layout: function(){
28990         
28991     },
28992     
28993     onRegionResized : function(region, newSize){
28994         this.fireEvent("regionresized", region, newSize);
28995         this.layout();
28996     },
28997     
28998     onRegionCollapsed : function(region){
28999         this.fireEvent("regioncollapsed", region);
29000     },
29001     
29002     onRegionExpanded : function(region){
29003         this.fireEvent("regionexpanded", region);
29004     },
29005         
29006     /**
29007      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29008      * performs box-model adjustments.
29009      * @return {Object} The size as an object {width: (the width), height: (the height)}
29010      */
29011     getViewSize : function(){
29012         var size;
29013         if(this.el.dom != document.body){
29014             size = this.el.getSize();
29015         }else{
29016             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29017         }
29018         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29019         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29020         return size;
29021     },
29022     
29023     /**
29024      * Returns the Element this layout is bound to.
29025      * @return {Roo.Element}
29026      */
29027     getEl : function(){
29028         return this.el;
29029     },
29030     
29031     /**
29032      * Returns the specified region.
29033      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29034      * @return {Roo.LayoutRegion}
29035      */
29036     getRegion : function(target){
29037         return this.regions[target.toLowerCase()];
29038     },
29039     
29040     onWindowResize : function(){
29041         if(this.monitorWindowResize){
29042             this.layout();
29043         }
29044     }
29045 });/*
29046  * Based on:
29047  * Ext JS Library 1.1.1
29048  * Copyright(c) 2006-2007, Ext JS, LLC.
29049  *
29050  * Originally Released Under LGPL - original licence link has changed is not relivant.
29051  *
29052  * Fork - LGPL
29053  * <script type="text/javascript">
29054  */
29055 /**
29056  * @class Roo.BorderLayout
29057  * @extends Roo.LayoutManager
29058  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29059  * please see: <br><br>
29060  * <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>
29061  * <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>
29062  * Example:
29063  <pre><code>
29064  var layout = new Roo.BorderLayout(document.body, {
29065     north: {
29066         initialSize: 25,
29067         titlebar: false
29068     },
29069     west: {
29070         split:true,
29071         initialSize: 200,
29072         minSize: 175,
29073         maxSize: 400,
29074         titlebar: true,
29075         collapsible: true
29076     },
29077     east: {
29078         split:true,
29079         initialSize: 202,
29080         minSize: 175,
29081         maxSize: 400,
29082         titlebar: true,
29083         collapsible: true
29084     },
29085     south: {
29086         split:true,
29087         initialSize: 100,
29088         minSize: 100,
29089         maxSize: 200,
29090         titlebar: true,
29091         collapsible: true
29092     },
29093     center: {
29094         titlebar: true,
29095         autoScroll:true,
29096         resizeTabs: true,
29097         minTabWidth: 50,
29098         preferredTabWidth: 150
29099     }
29100 });
29101
29102 // shorthand
29103 var CP = Roo.ContentPanel;
29104
29105 layout.beginUpdate();
29106 layout.add("north", new CP("north", "North"));
29107 layout.add("south", new CP("south", {title: "South", closable: true}));
29108 layout.add("west", new CP("west", {title: "West"}));
29109 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29110 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29111 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29112 layout.getRegion("center").showPanel("center1");
29113 layout.endUpdate();
29114 </code></pre>
29115
29116 <b>The container the layout is rendered into can be either the body element or any other element.
29117 If it is not the body element, the container needs to either be an absolute positioned element,
29118 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29119 the container size if it is not the body element.</b>
29120
29121 * @constructor
29122 * Create a new BorderLayout
29123 * @param {String/HTMLElement/Element} container The container this layout is bound to
29124 * @param {Object} config Configuration options
29125  */
29126 Roo.BorderLayout = function(container, config){
29127     config = config || {};
29128     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29129     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29130     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29131         var target = this.factory.validRegions[i];
29132         if(config[target]){
29133             this.addRegion(target, config[target]);
29134         }
29135     }
29136 };
29137
29138 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29139     /**
29140      * Creates and adds a new region if it doesn't already exist.
29141      * @param {String} target The target region key (north, south, east, west or center).
29142      * @param {Object} config The regions config object
29143      * @return {BorderLayoutRegion} The new region
29144      */
29145     addRegion : function(target, config){
29146         if(!this.regions[target]){
29147             var r = this.factory.create(target, this, config);
29148             this.bindRegion(target, r);
29149         }
29150         return this.regions[target];
29151     },
29152
29153     // private (kinda)
29154     bindRegion : function(name, r){
29155         this.regions[name] = r;
29156         r.on("visibilitychange", this.layout, this);
29157         r.on("paneladded", this.layout, this);
29158         r.on("panelremoved", this.layout, this);
29159         r.on("invalidated", this.layout, this);
29160         r.on("resized", this.onRegionResized, this);
29161         r.on("collapsed", this.onRegionCollapsed, this);
29162         r.on("expanded", this.onRegionExpanded, this);
29163     },
29164
29165     /**
29166      * Performs a layout update.
29167      */
29168     layout : function(){
29169         if(this.updating) return;
29170         var size = this.getViewSize();
29171         var w = size.width;
29172         var h = size.height;
29173         var centerW = w;
29174         var centerH = h;
29175         var centerY = 0;
29176         var centerX = 0;
29177         //var x = 0, y = 0;
29178
29179         var rs = this.regions;
29180         var north = rs["north"];
29181         var south = rs["south"]; 
29182         var west = rs["west"];
29183         var east = rs["east"];
29184         var center = rs["center"];
29185         //if(this.hideOnLayout){ // not supported anymore
29186             //c.el.setStyle("display", "none");
29187         //}
29188         if(north && north.isVisible()){
29189             var b = north.getBox();
29190             var m = north.getMargins();
29191             b.width = w - (m.left+m.right);
29192             b.x = m.left;
29193             b.y = m.top;
29194             centerY = b.height + b.y + m.bottom;
29195             centerH -= centerY;
29196             north.updateBox(this.safeBox(b));
29197         }
29198         if(south && south.isVisible()){
29199             var b = south.getBox();
29200             var m = south.getMargins();
29201             b.width = w - (m.left+m.right);
29202             b.x = m.left;
29203             var totalHeight = (b.height + m.top + m.bottom);
29204             b.y = h - totalHeight + m.top;
29205             centerH -= totalHeight;
29206             south.updateBox(this.safeBox(b));
29207         }
29208         if(west && west.isVisible()){
29209             var b = west.getBox();
29210             var m = west.getMargins();
29211             b.height = centerH - (m.top+m.bottom);
29212             b.x = m.left;
29213             b.y = centerY + m.top;
29214             var totalWidth = (b.width + m.left + m.right);
29215             centerX += totalWidth;
29216             centerW -= totalWidth;
29217             west.updateBox(this.safeBox(b));
29218         }
29219         if(east && east.isVisible()){
29220             var b = east.getBox();
29221             var m = east.getMargins();
29222             b.height = centerH - (m.top+m.bottom);
29223             var totalWidth = (b.width + m.left + m.right);
29224             b.x = w - totalWidth + m.left;
29225             b.y = centerY + m.top;
29226             centerW -= totalWidth;
29227             east.updateBox(this.safeBox(b));
29228         }
29229         if(center){
29230             var m = center.getMargins();
29231             var centerBox = {
29232                 x: centerX + m.left,
29233                 y: centerY + m.top,
29234                 width: centerW - (m.left+m.right),
29235                 height: centerH - (m.top+m.bottom)
29236             };
29237             //if(this.hideOnLayout){
29238                 //center.el.setStyle("display", "block");
29239             //}
29240             center.updateBox(this.safeBox(centerBox));
29241         }
29242         this.el.repaint();
29243         this.fireEvent("layout", this);
29244     },
29245
29246     // private
29247     safeBox : function(box){
29248         box.width = Math.max(0, box.width);
29249         box.height = Math.max(0, box.height);
29250         return box;
29251     },
29252
29253     /**
29254      * Adds a ContentPanel (or subclass) to this layout.
29255      * @param {String} target The target region key (north, south, east, west or center).
29256      * @param {Roo.ContentPanel} panel The panel to add
29257      * @return {Roo.ContentPanel} The added panel
29258      */
29259     add : function(target, panel){
29260          
29261         target = target.toLowerCase();
29262         return this.regions[target].add(panel);
29263     },
29264
29265     /**
29266      * Remove a ContentPanel (or subclass) to this layout.
29267      * @param {String} target The target region key (north, south, east, west or center).
29268      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29269      * @return {Roo.ContentPanel} The removed panel
29270      */
29271     remove : function(target, panel){
29272         target = target.toLowerCase();
29273         return this.regions[target].remove(panel);
29274     },
29275
29276     /**
29277      * Searches all regions for a panel with the specified id
29278      * @param {String} panelId
29279      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29280      */
29281     findPanel : function(panelId){
29282         var rs = this.regions;
29283         for(var target in rs){
29284             if(typeof rs[target] != "function"){
29285                 var p = rs[target].getPanel(panelId);
29286                 if(p){
29287                     return p;
29288                 }
29289             }
29290         }
29291         return null;
29292     },
29293
29294     /**
29295      * Searches all regions for a panel with the specified id and activates (shows) it.
29296      * @param {String/ContentPanel} panelId The panels id or the panel itself
29297      * @return {Roo.ContentPanel} The shown panel or null
29298      */
29299     showPanel : function(panelId) {
29300       var rs = this.regions;
29301       for(var target in rs){
29302          var r = rs[target];
29303          if(typeof r != "function"){
29304             if(r.hasPanel(panelId)){
29305                return r.showPanel(panelId);
29306             }
29307          }
29308       }
29309       return null;
29310    },
29311
29312    /**
29313      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29314      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29315      */
29316     restoreState : function(provider){
29317         if(!provider){
29318             provider = Roo.state.Manager;
29319         }
29320         var sm = new Roo.LayoutStateManager();
29321         sm.init(this, provider);
29322     },
29323
29324     /**
29325      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29326      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29327      * a valid ContentPanel config object.  Example:
29328      * <pre><code>
29329 // Create the main layout
29330 var layout = new Roo.BorderLayout('main-ct', {
29331     west: {
29332         split:true,
29333         minSize: 175,
29334         titlebar: true
29335     },
29336     center: {
29337         title:'Components'
29338     }
29339 }, 'main-ct');
29340
29341 // Create and add multiple ContentPanels at once via configs
29342 layout.batchAdd({
29343    west: {
29344        id: 'source-files',
29345        autoCreate:true,
29346        title:'Ext Source Files',
29347        autoScroll:true,
29348        fitToFrame:true
29349    },
29350    center : {
29351        el: cview,
29352        autoScroll:true,
29353        fitToFrame:true,
29354        toolbar: tb,
29355        resizeEl:'cbody'
29356    }
29357 });
29358 </code></pre>
29359      * @param {Object} regions An object containing ContentPanel configs by region name
29360      */
29361     batchAdd : function(regions){
29362         this.beginUpdate();
29363         for(var rname in regions){
29364             var lr = this.regions[rname];
29365             if(lr){
29366                 this.addTypedPanels(lr, regions[rname]);
29367             }
29368         }
29369         this.endUpdate();
29370     },
29371
29372     // private
29373     addTypedPanels : function(lr, ps){
29374         if(typeof ps == 'string'){
29375             lr.add(new Roo.ContentPanel(ps));
29376         }
29377         else if(ps instanceof Array){
29378             for(var i =0, len = ps.length; i < len; i++){
29379                 this.addTypedPanels(lr, ps[i]);
29380             }
29381         }
29382         else if(!ps.events){ // raw config?
29383             var el = ps.el;
29384             delete ps.el; // prevent conflict
29385             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29386         }
29387         else {  // panel object assumed!
29388             lr.add(ps);
29389         }
29390     },
29391     /**
29392      * Adds a xtype elements to the layout.
29393      * <pre><code>
29394
29395 layout.addxtype({
29396        xtype : 'ContentPanel',
29397        region: 'west',
29398        items: [ .... ]
29399    }
29400 );
29401
29402 layout.addxtype({
29403         xtype : 'NestedLayoutPanel',
29404         region: 'west',
29405         layout: {
29406            center: { },
29407            west: { }   
29408         },
29409         items : [ ... list of content panels or nested layout panels.. ]
29410    }
29411 );
29412 </code></pre>
29413      * @param {Object} cfg Xtype definition of item to add.
29414      */
29415     addxtype : function(cfg)
29416     {
29417         // basically accepts a pannel...
29418         // can accept a layout region..!?!?
29419        // console.log('BorderLayout add ' + cfg.xtype)
29420         
29421         if (!cfg.xtype.match(/Panel$/)) {
29422             return false;
29423         }
29424         var ret = false;
29425         var region = cfg.region;
29426         delete cfg.region;
29427         
29428           
29429         var xitems = [];
29430         if (cfg.items) {
29431             xitems = cfg.items;
29432             delete cfg.items;
29433         }
29434         
29435         
29436         switch(cfg.xtype) 
29437         {
29438             case 'ContentPanel':  // ContentPanel (el, cfg)
29439             case 'ScrollPanel':  // ContentPanel (el, cfg)
29440                 if(cfg.autoCreate) {
29441                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29442                 } else {
29443                     var el = this.el.createChild();
29444                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29445                 }
29446                 
29447                 this.add(region, ret);
29448                 break;
29449             
29450             
29451             case 'TreePanel': // our new panel!
29452                 cfg.el = this.el.createChild();
29453                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29454                 this.add(region, ret);
29455                 break;
29456             
29457             case 'NestedLayoutPanel': 
29458                 // create a new Layout (which is  a Border Layout...
29459                 var el = this.el.createChild();
29460                 var clayout = cfg.layout;
29461                 delete cfg.layout;
29462                 clayout.items   = clayout.items  || [];
29463                 // replace this exitems with the clayout ones..
29464                 xitems = clayout.items;
29465                  
29466                 
29467                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29468                     cfg.background = false;
29469                 }
29470                 var layout = new Roo.BorderLayout(el, clayout);
29471                 
29472                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29473                 //console.log('adding nested layout panel '  + cfg.toSource());
29474                 this.add(region, ret);
29475                 
29476                 break;
29477                 
29478             case 'GridPanel': 
29479             
29480                 // needs grid and region
29481                 
29482                 //var el = this.getRegion(region).el.createChild();
29483                 var el = this.el.createChild();
29484                 // create the grid first...
29485                 
29486                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29487                 delete cfg.grid;
29488                 if (region == 'center' && this.active ) {
29489                     cfg.background = false;
29490                 }
29491                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29492                 
29493                 this.add(region, ret);
29494                 if (cfg.background) {
29495                     ret.on('activate', function(gp) {
29496                         if (!gp.grid.rendered) {
29497                             gp.grid.render();
29498                         }
29499                     });
29500                 } else {
29501                     grid.render();
29502                 }
29503                 break;
29504            
29505                
29506                 
29507                 
29508             default: 
29509                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29510                 return null;
29511              // GridPanel (grid, cfg)
29512             
29513         }
29514         this.beginUpdate();
29515         // add children..
29516         Roo.each(xitems, function(i)  {
29517             ret.addxtype(i);
29518         });
29519         this.endUpdate();
29520         return ret;
29521         
29522     }
29523 });
29524
29525 /**
29526  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29527  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29528  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29529  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29530  * <pre><code>
29531 // shorthand
29532 var CP = Roo.ContentPanel;
29533
29534 var layout = Roo.BorderLayout.create({
29535     north: {
29536         initialSize: 25,
29537         titlebar: false,
29538         panels: [new CP("north", "North")]
29539     },
29540     west: {
29541         split:true,
29542         initialSize: 200,
29543         minSize: 175,
29544         maxSize: 400,
29545         titlebar: true,
29546         collapsible: true,
29547         panels: [new CP("west", {title: "West"})]
29548     },
29549     east: {
29550         split:true,
29551         initialSize: 202,
29552         minSize: 175,
29553         maxSize: 400,
29554         titlebar: true,
29555         collapsible: true,
29556         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29557     },
29558     south: {
29559         split:true,
29560         initialSize: 100,
29561         minSize: 100,
29562         maxSize: 200,
29563         titlebar: true,
29564         collapsible: true,
29565         panels: [new CP("south", {title: "South", closable: true})]
29566     },
29567     center: {
29568         titlebar: true,
29569         autoScroll:true,
29570         resizeTabs: true,
29571         minTabWidth: 50,
29572         preferredTabWidth: 150,
29573         panels: [
29574             new CP("center1", {title: "Close Me", closable: true}),
29575             new CP("center2", {title: "Center Panel", closable: false})
29576         ]
29577     }
29578 }, document.body);
29579
29580 layout.getRegion("center").showPanel("center1");
29581 </code></pre>
29582  * @param config
29583  * @param targetEl
29584  */
29585 Roo.BorderLayout.create = function(config, targetEl){
29586     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29587     layout.beginUpdate();
29588     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29589     for(var j = 0, jlen = regions.length; j < jlen; j++){
29590         var lr = regions[j];
29591         if(layout.regions[lr] && config[lr].panels){
29592             var r = layout.regions[lr];
29593             var ps = config[lr].panels;
29594             layout.addTypedPanels(r, ps);
29595         }
29596     }
29597     layout.endUpdate();
29598     return layout;
29599 };
29600
29601 // private
29602 Roo.BorderLayout.RegionFactory = {
29603     // private
29604     validRegions : ["north","south","east","west","center"],
29605
29606     // private
29607     create : function(target, mgr, config){
29608         target = target.toLowerCase();
29609         if(config.lightweight || config.basic){
29610             return new Roo.BasicLayoutRegion(mgr, config, target);
29611         }
29612         switch(target){
29613             case "north":
29614                 return new Roo.NorthLayoutRegion(mgr, config);
29615             case "south":
29616                 return new Roo.SouthLayoutRegion(mgr, config);
29617             case "east":
29618                 return new Roo.EastLayoutRegion(mgr, config);
29619             case "west":
29620                 return new Roo.WestLayoutRegion(mgr, config);
29621             case "center":
29622                 return new Roo.CenterLayoutRegion(mgr, config);
29623         }
29624         throw 'Layout region "'+target+'" not supported.';
29625     }
29626 };/*
29627  * Based on:
29628  * Ext JS Library 1.1.1
29629  * Copyright(c) 2006-2007, Ext JS, LLC.
29630  *
29631  * Originally Released Under LGPL - original licence link has changed is not relivant.
29632  *
29633  * Fork - LGPL
29634  * <script type="text/javascript">
29635  */
29636  
29637 /**
29638  * @class Roo.BasicLayoutRegion
29639  * @extends Roo.util.Observable
29640  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29641  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29642  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29643  */
29644 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29645     this.mgr = mgr;
29646     this.position  = pos;
29647     this.events = {
29648         /**
29649          * @scope Roo.BasicLayoutRegion
29650          */
29651         
29652         /**
29653          * @event beforeremove
29654          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29655          * @param {Roo.LayoutRegion} this
29656          * @param {Roo.ContentPanel} panel The panel
29657          * @param {Object} e The cancel event object
29658          */
29659         "beforeremove" : true,
29660         /**
29661          * @event invalidated
29662          * Fires when the layout for this region is changed.
29663          * @param {Roo.LayoutRegion} this
29664          */
29665         "invalidated" : true,
29666         /**
29667          * @event visibilitychange
29668          * Fires when this region is shown or hidden 
29669          * @param {Roo.LayoutRegion} this
29670          * @param {Boolean} visibility true or false
29671          */
29672         "visibilitychange" : true,
29673         /**
29674          * @event paneladded
29675          * Fires when a panel is added. 
29676          * @param {Roo.LayoutRegion} this
29677          * @param {Roo.ContentPanel} panel The panel
29678          */
29679         "paneladded" : true,
29680         /**
29681          * @event panelremoved
29682          * Fires when a panel is removed. 
29683          * @param {Roo.LayoutRegion} this
29684          * @param {Roo.ContentPanel} panel The panel
29685          */
29686         "panelremoved" : true,
29687         /**
29688          * @event collapsed
29689          * Fires when this region is collapsed.
29690          * @param {Roo.LayoutRegion} this
29691          */
29692         "collapsed" : true,
29693         /**
29694          * @event expanded
29695          * Fires when this region is expanded.
29696          * @param {Roo.LayoutRegion} this
29697          */
29698         "expanded" : true,
29699         /**
29700          * @event slideshow
29701          * Fires when this region is slid into view.
29702          * @param {Roo.LayoutRegion} this
29703          */
29704         "slideshow" : true,
29705         /**
29706          * @event slidehide
29707          * Fires when this region slides out of view. 
29708          * @param {Roo.LayoutRegion} this
29709          */
29710         "slidehide" : true,
29711         /**
29712          * @event panelactivated
29713          * Fires when a panel is activated. 
29714          * @param {Roo.LayoutRegion} this
29715          * @param {Roo.ContentPanel} panel The activated panel
29716          */
29717         "panelactivated" : true,
29718         /**
29719          * @event resized
29720          * Fires when the user resizes this region. 
29721          * @param {Roo.LayoutRegion} this
29722          * @param {Number} newSize The new size (width for east/west, height for north/south)
29723          */
29724         "resized" : true
29725     };
29726     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29727     this.panels = new Roo.util.MixedCollection();
29728     this.panels.getKey = this.getPanelId.createDelegate(this);
29729     this.box = null;
29730     this.activePanel = null;
29731     // ensure listeners are added...
29732     
29733     if (config.listeners || config.events) {
29734         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29735             listeners : config.listeners || {},
29736             events : config.events || {}
29737         });
29738     }
29739     
29740     if(skipConfig !== true){
29741         this.applyConfig(config);
29742     }
29743 };
29744
29745 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29746     getPanelId : function(p){
29747         return p.getId();
29748     },
29749     
29750     applyConfig : function(config){
29751         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29752         this.config = config;
29753         
29754     },
29755     
29756     /**
29757      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29758      * the width, for horizontal (north, south) the height.
29759      * @param {Number} newSize The new width or height
29760      */
29761     resizeTo : function(newSize){
29762         var el = this.el ? this.el :
29763                  (this.activePanel ? this.activePanel.getEl() : null);
29764         if(el){
29765             switch(this.position){
29766                 case "east":
29767                 case "west":
29768                     el.setWidth(newSize);
29769                     this.fireEvent("resized", this, newSize);
29770                 break;
29771                 case "north":
29772                 case "south":
29773                     el.setHeight(newSize);
29774                     this.fireEvent("resized", this, newSize);
29775                 break;                
29776             }
29777         }
29778     },
29779     
29780     getBox : function(){
29781         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29782     },
29783     
29784     getMargins : function(){
29785         return this.margins;
29786     },
29787     
29788     updateBox : function(box){
29789         this.box = box;
29790         var el = this.activePanel.getEl();
29791         el.dom.style.left = box.x + "px";
29792         el.dom.style.top = box.y + "px";
29793         this.activePanel.setSize(box.width, box.height);
29794     },
29795     
29796     /**
29797      * Returns the container element for this region.
29798      * @return {Roo.Element}
29799      */
29800     getEl : function(){
29801         return this.activePanel;
29802     },
29803     
29804     /**
29805      * Returns true if this region is currently visible.
29806      * @return {Boolean}
29807      */
29808     isVisible : function(){
29809         return this.activePanel ? true : false;
29810     },
29811     
29812     setActivePanel : function(panel){
29813         panel = this.getPanel(panel);
29814         if(this.activePanel && this.activePanel != panel){
29815             this.activePanel.setActiveState(false);
29816             this.activePanel.getEl().setLeftTop(-10000,-10000);
29817         }
29818         this.activePanel = panel;
29819         panel.setActiveState(true);
29820         if(this.box){
29821             panel.setSize(this.box.width, this.box.height);
29822         }
29823         this.fireEvent("panelactivated", this, panel);
29824         this.fireEvent("invalidated");
29825     },
29826     
29827     /**
29828      * Show the specified panel.
29829      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29830      * @return {Roo.ContentPanel} The shown panel or null
29831      */
29832     showPanel : function(panel){
29833         if(panel = this.getPanel(panel)){
29834             this.setActivePanel(panel);
29835         }
29836         return panel;
29837     },
29838     
29839     /**
29840      * Get the active panel for this region.
29841      * @return {Roo.ContentPanel} The active panel or null
29842      */
29843     getActivePanel : function(){
29844         return this.activePanel;
29845     },
29846     
29847     /**
29848      * Add the passed ContentPanel(s)
29849      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29850      * @return {Roo.ContentPanel} The panel added (if only one was added)
29851      */
29852     add : function(panel){
29853         if(arguments.length > 1){
29854             for(var i = 0, len = arguments.length; i < len; i++) {
29855                 this.add(arguments[i]);
29856             }
29857             return null;
29858         }
29859         if(this.hasPanel(panel)){
29860             this.showPanel(panel);
29861             return panel;
29862         }
29863         var el = panel.getEl();
29864         if(el.dom.parentNode != this.mgr.el.dom){
29865             this.mgr.el.dom.appendChild(el.dom);
29866         }
29867         if(panel.setRegion){
29868             panel.setRegion(this);
29869         }
29870         this.panels.add(panel);
29871         el.setStyle("position", "absolute");
29872         if(!panel.background){
29873             this.setActivePanel(panel);
29874             if(this.config.initialSize && this.panels.getCount()==1){
29875                 this.resizeTo(this.config.initialSize);
29876             }
29877         }
29878         this.fireEvent("paneladded", this, panel);
29879         return panel;
29880     },
29881     
29882     /**
29883      * Returns true if the panel is in this region.
29884      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29885      * @return {Boolean}
29886      */
29887     hasPanel : function(panel){
29888         if(typeof panel == "object"){ // must be panel obj
29889             panel = panel.getId();
29890         }
29891         return this.getPanel(panel) ? true : false;
29892     },
29893     
29894     /**
29895      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29896      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29897      * @param {Boolean} preservePanel Overrides the config preservePanel option
29898      * @return {Roo.ContentPanel} The panel that was removed
29899      */
29900     remove : function(panel, preservePanel){
29901         panel = this.getPanel(panel);
29902         if(!panel){
29903             return null;
29904         }
29905         var e = {};
29906         this.fireEvent("beforeremove", this, panel, e);
29907         if(e.cancel === true){
29908             return null;
29909         }
29910         var panelId = panel.getId();
29911         this.panels.removeKey(panelId);
29912         return panel;
29913     },
29914     
29915     /**
29916      * Returns the panel specified or null if it's not in this region.
29917      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29918      * @return {Roo.ContentPanel}
29919      */
29920     getPanel : function(id){
29921         if(typeof id == "object"){ // must be panel obj
29922             return id;
29923         }
29924         return this.panels.get(id);
29925     },
29926     
29927     /**
29928      * Returns this regions position (north/south/east/west/center).
29929      * @return {String} 
29930      */
29931     getPosition: function(){
29932         return this.position;    
29933     }
29934 });/*
29935  * Based on:
29936  * Ext JS Library 1.1.1
29937  * Copyright(c) 2006-2007, Ext JS, LLC.
29938  *
29939  * Originally Released Under LGPL - original licence link has changed is not relivant.
29940  *
29941  * Fork - LGPL
29942  * <script type="text/javascript">
29943  */
29944  
29945 /**
29946  * @class Roo.LayoutRegion
29947  * @extends Roo.BasicLayoutRegion
29948  * This class represents a region in a layout manager.
29949  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29950  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29951  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29952  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29953  * @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})
29954  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
29955  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29956  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29957  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29958  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29959  * @cfg {String}    title           The title for the region (overrides panel titles)
29960  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29961  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29962  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29963  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29964  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29965  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29966  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29967  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29968  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29969  * @cfg {Boolean}   showPin         True to show a pin button
29970  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29971  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29972  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29973  * @cfg {Number}    width           For East/West panels
29974  * @cfg {Number}    height          For North/South panels
29975  * @cfg {Boolean}   split           To show the splitter
29976  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29977  */
29978 Roo.LayoutRegion = function(mgr, config, pos){
29979     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29980     var dh = Roo.DomHelper;
29981     /** This region's container element 
29982     * @type Roo.Element */
29983     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29984     /** This region's title element 
29985     * @type Roo.Element */
29986
29987     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29988         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29989         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29990     ]}, true);
29991     this.titleEl.enableDisplayMode();
29992     /** This region's title text element 
29993     * @type HTMLElement */
29994     this.titleTextEl = this.titleEl.dom.firstChild;
29995     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29996     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29997     this.closeBtn.enableDisplayMode();
29998     this.closeBtn.on("click", this.closeClicked, this);
29999     this.closeBtn.hide();
30000
30001     this.createBody(config);
30002     this.visible = true;
30003     this.collapsed = false;
30004
30005     if(config.hideWhenEmpty){
30006         this.hide();
30007         this.on("paneladded", this.validateVisibility, this);
30008         this.on("panelremoved", this.validateVisibility, this);
30009     }
30010     this.applyConfig(config);
30011 };
30012
30013 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30014
30015     createBody : function(){
30016         /** This region's body element 
30017         * @type Roo.Element */
30018         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30019     },
30020
30021     applyConfig : function(c){
30022         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30023             var dh = Roo.DomHelper;
30024             if(c.titlebar !== false){
30025                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30026                 this.collapseBtn.on("click", this.collapse, this);
30027                 this.collapseBtn.enableDisplayMode();
30028
30029                 if(c.showPin === true || this.showPin){
30030                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30031                     this.stickBtn.enableDisplayMode();
30032                     this.stickBtn.on("click", this.expand, this);
30033                     this.stickBtn.hide();
30034                 }
30035             }
30036             /** This region's collapsed element
30037             * @type Roo.Element */
30038             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30039                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30040             ]}, true);
30041             if(c.floatable !== false){
30042                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30043                this.collapsedEl.on("click", this.collapseClick, this);
30044             }
30045
30046             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30047                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30048                    id: "message", unselectable: "on", style:{"float":"left"}});
30049                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30050              }
30051             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30052             this.expandBtn.on("click", this.expand, this);
30053         }
30054         if(this.collapseBtn){
30055             this.collapseBtn.setVisible(c.collapsible == true);
30056         }
30057         this.cmargins = c.cmargins || this.cmargins ||
30058                          (this.position == "west" || this.position == "east" ?
30059                              {top: 0, left: 2, right:2, bottom: 0} :
30060                              {top: 2, left: 0, right:0, bottom: 2});
30061         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30062         this.bottomTabs = c.tabPosition != "top";
30063         this.autoScroll = c.autoScroll || false;
30064         if(this.autoScroll){
30065             this.bodyEl.setStyle("overflow", "auto");
30066         }else{
30067             this.bodyEl.setStyle("overflow", "hidden");
30068         }
30069         //if(c.titlebar !== false){
30070             if((!c.titlebar && !c.title) || c.titlebar === false){
30071                 this.titleEl.hide();
30072             }else{
30073                 this.titleEl.show();
30074                 if(c.title){
30075                     this.titleTextEl.innerHTML = c.title;
30076                 }
30077             }
30078         //}
30079         this.duration = c.duration || .30;
30080         this.slideDuration = c.slideDuration || .45;
30081         this.config = c;
30082         if(c.collapsed){
30083             this.collapse(true);
30084         }
30085         if(c.hidden){
30086             this.hide();
30087         }
30088     },
30089     /**
30090      * Returns true if this region is currently visible.
30091      * @return {Boolean}
30092      */
30093     isVisible : function(){
30094         return this.visible;
30095     },
30096
30097     /**
30098      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30099      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30100      */
30101     setCollapsedTitle : function(title){
30102         title = title || "&#160;";
30103         if(this.collapsedTitleTextEl){
30104             this.collapsedTitleTextEl.innerHTML = title;
30105         }
30106     },
30107
30108     getBox : function(){
30109         var b;
30110         if(!this.collapsed){
30111             b = this.el.getBox(false, true);
30112         }else{
30113             b = this.collapsedEl.getBox(false, true);
30114         }
30115         return b;
30116     },
30117
30118     getMargins : function(){
30119         return this.collapsed ? this.cmargins : this.margins;
30120     },
30121
30122     highlight : function(){
30123         this.el.addClass("x-layout-panel-dragover");
30124     },
30125
30126     unhighlight : function(){
30127         this.el.removeClass("x-layout-panel-dragover");
30128     },
30129
30130     updateBox : function(box){
30131         this.box = box;
30132         if(!this.collapsed){
30133             this.el.dom.style.left = box.x + "px";
30134             this.el.dom.style.top = box.y + "px";
30135             this.updateBody(box.width, box.height);
30136         }else{
30137             this.collapsedEl.dom.style.left = box.x + "px";
30138             this.collapsedEl.dom.style.top = box.y + "px";
30139             this.collapsedEl.setSize(box.width, box.height);
30140         }
30141         if(this.tabs){
30142             this.tabs.autoSizeTabs();
30143         }
30144     },
30145
30146     updateBody : function(w, h){
30147         if(w !== null){
30148             this.el.setWidth(w);
30149             w -= this.el.getBorderWidth("rl");
30150             if(this.config.adjustments){
30151                 w += this.config.adjustments[0];
30152             }
30153         }
30154         if(h !== null){
30155             this.el.setHeight(h);
30156             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30157             h -= this.el.getBorderWidth("tb");
30158             if(this.config.adjustments){
30159                 h += this.config.adjustments[1];
30160             }
30161             this.bodyEl.setHeight(h);
30162             if(this.tabs){
30163                 h = this.tabs.syncHeight(h);
30164             }
30165         }
30166         if(this.panelSize){
30167             w = w !== null ? w : this.panelSize.width;
30168             h = h !== null ? h : this.panelSize.height;
30169         }
30170         if(this.activePanel){
30171             var el = this.activePanel.getEl();
30172             w = w !== null ? w : el.getWidth();
30173             h = h !== null ? h : el.getHeight();
30174             this.panelSize = {width: w, height: h};
30175             this.activePanel.setSize(w, h);
30176         }
30177         if(Roo.isIE && this.tabs){
30178             this.tabs.el.repaint();
30179         }
30180     },
30181
30182     /**
30183      * Returns the container element for this region.
30184      * @return {Roo.Element}
30185      */
30186     getEl : function(){
30187         return this.el;
30188     },
30189
30190     /**
30191      * Hides this region.
30192      */
30193     hide : function(){
30194         if(!this.collapsed){
30195             this.el.dom.style.left = "-2000px";
30196             this.el.hide();
30197         }else{
30198             this.collapsedEl.dom.style.left = "-2000px";
30199             this.collapsedEl.hide();
30200         }
30201         this.visible = false;
30202         this.fireEvent("visibilitychange", this, false);
30203     },
30204
30205     /**
30206      * Shows this region if it was previously hidden.
30207      */
30208     show : function(){
30209         if(!this.collapsed){
30210             this.el.show();
30211         }else{
30212             this.collapsedEl.show();
30213         }
30214         this.visible = true;
30215         this.fireEvent("visibilitychange", this, true);
30216     },
30217
30218     closeClicked : function(){
30219         if(this.activePanel){
30220             this.remove(this.activePanel);
30221         }
30222     },
30223
30224     collapseClick : function(e){
30225         if(this.isSlid){
30226            e.stopPropagation();
30227            this.slideIn();
30228         }else{
30229            e.stopPropagation();
30230            this.slideOut();
30231         }
30232     },
30233
30234     /**
30235      * Collapses this region.
30236      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30237      */
30238     collapse : function(skipAnim){
30239         if(this.collapsed) return;
30240         this.collapsed = true;
30241         if(this.split){
30242             this.split.el.hide();
30243         }
30244         if(this.config.animate && skipAnim !== true){
30245             this.fireEvent("invalidated", this);
30246             this.animateCollapse();
30247         }else{
30248             this.el.setLocation(-20000,-20000);
30249             this.el.hide();
30250             this.collapsedEl.show();
30251             this.fireEvent("collapsed", this);
30252             this.fireEvent("invalidated", this);
30253         }
30254     },
30255
30256     animateCollapse : function(){
30257         // overridden
30258     },
30259
30260     /**
30261      * Expands this region if it was previously collapsed.
30262      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30263      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30264      */
30265     expand : function(e, skipAnim){
30266         if(e) e.stopPropagation();
30267         if(!this.collapsed || this.el.hasActiveFx()) return;
30268         if(this.isSlid){
30269             this.afterSlideIn();
30270             skipAnim = true;
30271         }
30272         this.collapsed = false;
30273         if(this.config.animate && skipAnim !== true){
30274             this.animateExpand();
30275         }else{
30276             this.el.show();
30277             if(this.split){
30278                 this.split.el.show();
30279             }
30280             this.collapsedEl.setLocation(-2000,-2000);
30281             this.collapsedEl.hide();
30282             this.fireEvent("invalidated", this);
30283             this.fireEvent("expanded", this);
30284         }
30285     },
30286
30287     animateExpand : function(){
30288         // overridden
30289     },
30290
30291     initTabs : function()
30292     {
30293         this.bodyEl.setStyle("overflow", "hidden");
30294         var ts = new Roo.TabPanel(
30295                 this.bodyEl.dom,
30296                 {
30297                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30298                     disableTooltips: this.config.disableTabTips,
30299                     toolbar : this.config.toolbar
30300                 }
30301         );
30302         if(this.config.hideTabs){
30303             ts.stripWrap.setDisplayed(false);
30304         }
30305         this.tabs = ts;
30306         ts.resizeTabs = this.config.resizeTabs === true;
30307         ts.minTabWidth = this.config.minTabWidth || 40;
30308         ts.maxTabWidth = this.config.maxTabWidth || 250;
30309         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30310         ts.monitorResize = false;
30311         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30312         ts.bodyEl.addClass('x-layout-tabs-body');
30313         this.panels.each(this.initPanelAsTab, this);
30314     },
30315
30316     initPanelAsTab : function(panel){
30317         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30318                     this.config.closeOnTab && panel.isClosable());
30319         if(panel.tabTip !== undefined){
30320             ti.setTooltip(panel.tabTip);
30321         }
30322         ti.on("activate", function(){
30323               this.setActivePanel(panel);
30324         }, this);
30325         if(this.config.closeOnTab){
30326             ti.on("beforeclose", function(t, e){
30327                 e.cancel = true;
30328                 this.remove(panel);
30329             }, this);
30330         }
30331         return ti;
30332     },
30333
30334     updatePanelTitle : function(panel, title){
30335         if(this.activePanel == panel){
30336             this.updateTitle(title);
30337         }
30338         if(this.tabs){
30339             var ti = this.tabs.getTab(panel.getEl().id);
30340             ti.setText(title);
30341             if(panel.tabTip !== undefined){
30342                 ti.setTooltip(panel.tabTip);
30343             }
30344         }
30345     },
30346
30347     updateTitle : function(title){
30348         if(this.titleTextEl && !this.config.title){
30349             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30350         }
30351     },
30352
30353     setActivePanel : function(panel){
30354         panel = this.getPanel(panel);
30355         if(this.activePanel && this.activePanel != panel){
30356             this.activePanel.setActiveState(false);
30357         }
30358         this.activePanel = panel;
30359         panel.setActiveState(true);
30360         if(this.panelSize){
30361             panel.setSize(this.panelSize.width, this.panelSize.height);
30362         }
30363         if(this.closeBtn){
30364             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30365         }
30366         this.updateTitle(panel.getTitle());
30367         if(this.tabs){
30368             this.fireEvent("invalidated", this);
30369         }
30370         this.fireEvent("panelactivated", this, panel);
30371     },
30372
30373     /**
30374      * Shows the specified panel.
30375      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30376      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30377      */
30378     showPanel : function(panel){
30379         if(panel = this.getPanel(panel)){
30380             if(this.tabs){
30381                 var tab = this.tabs.getTab(panel.getEl().id);
30382                 if(tab.isHidden()){
30383                     this.tabs.unhideTab(tab.id);
30384                 }
30385                 tab.activate();
30386             }else{
30387                 this.setActivePanel(panel);
30388             }
30389         }
30390         return panel;
30391     },
30392
30393     /**
30394      * Get the active panel for this region.
30395      * @return {Roo.ContentPanel} The active panel or null
30396      */
30397     getActivePanel : function(){
30398         return this.activePanel;
30399     },
30400
30401     validateVisibility : function(){
30402         if(this.panels.getCount() < 1){
30403             this.updateTitle("&#160;");
30404             this.closeBtn.hide();
30405             this.hide();
30406         }else{
30407             if(!this.isVisible()){
30408                 this.show();
30409             }
30410         }
30411     },
30412
30413     /**
30414      * Adds the passed ContentPanel(s) to this region.
30415      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30416      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30417      */
30418     add : function(panel){
30419         if(arguments.length > 1){
30420             for(var i = 0, len = arguments.length; i < len; i++) {
30421                 this.add(arguments[i]);
30422             }
30423             return null;
30424         }
30425         if(this.hasPanel(panel)){
30426             this.showPanel(panel);
30427             return panel;
30428         }
30429         panel.setRegion(this);
30430         this.panels.add(panel);
30431         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30432             this.bodyEl.dom.appendChild(panel.getEl().dom);
30433             if(panel.background !== true){
30434                 this.setActivePanel(panel);
30435             }
30436             this.fireEvent("paneladded", this, panel);
30437             return panel;
30438         }
30439         if(!this.tabs){
30440             this.initTabs();
30441         }else{
30442             this.initPanelAsTab(panel);
30443         }
30444         if(panel.background !== true){
30445             this.tabs.activate(panel.getEl().id);
30446         }
30447         this.fireEvent("paneladded", this, panel);
30448         return panel;
30449     },
30450
30451     /**
30452      * Hides the tab for the specified panel.
30453      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30454      */
30455     hidePanel : function(panel){
30456         if(this.tabs && (panel = this.getPanel(panel))){
30457             this.tabs.hideTab(panel.getEl().id);
30458         }
30459     },
30460
30461     /**
30462      * Unhides the tab for a previously hidden panel.
30463      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30464      */
30465     unhidePanel : function(panel){
30466         if(this.tabs && (panel = this.getPanel(panel))){
30467             this.tabs.unhideTab(panel.getEl().id);
30468         }
30469     },
30470
30471     clearPanels : function(){
30472         while(this.panels.getCount() > 0){
30473              this.remove(this.panels.first());
30474         }
30475     },
30476
30477     /**
30478      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30479      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30480      * @param {Boolean} preservePanel Overrides the config preservePanel option
30481      * @return {Roo.ContentPanel} The panel that was removed
30482      */
30483     remove : function(panel, preservePanel){
30484         panel = this.getPanel(panel);
30485         if(!panel){
30486             return null;
30487         }
30488         var e = {};
30489         this.fireEvent("beforeremove", this, panel, e);
30490         if(e.cancel === true){
30491             return null;
30492         }
30493         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30494         var panelId = panel.getId();
30495         this.panels.removeKey(panelId);
30496         if(preservePanel){
30497             document.body.appendChild(panel.getEl().dom);
30498         }
30499         if(this.tabs){
30500             this.tabs.removeTab(panel.getEl().id);
30501         }else if (!preservePanel){
30502             this.bodyEl.dom.removeChild(panel.getEl().dom);
30503         }
30504         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30505             var p = this.panels.first();
30506             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30507             tempEl.appendChild(p.getEl().dom);
30508             this.bodyEl.update("");
30509             this.bodyEl.dom.appendChild(p.getEl().dom);
30510             tempEl = null;
30511             this.updateTitle(p.getTitle());
30512             this.tabs = null;
30513             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30514             this.setActivePanel(p);
30515         }
30516         panel.setRegion(null);
30517         if(this.activePanel == panel){
30518             this.activePanel = null;
30519         }
30520         if(this.config.autoDestroy !== false && preservePanel !== true){
30521             try{panel.destroy();}catch(e){}
30522         }
30523         this.fireEvent("panelremoved", this, panel);
30524         return panel;
30525     },
30526
30527     /**
30528      * Returns the TabPanel component used by this region
30529      * @return {Roo.TabPanel}
30530      */
30531     getTabs : function(){
30532         return this.tabs;
30533     },
30534
30535     createTool : function(parentEl, className){
30536         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30537             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30538         btn.addClassOnOver("x-layout-tools-button-over");
30539         return btn;
30540     }
30541 });/*
30542  * Based on:
30543  * Ext JS Library 1.1.1
30544  * Copyright(c) 2006-2007, Ext JS, LLC.
30545  *
30546  * Originally Released Under LGPL - original licence link has changed is not relivant.
30547  *
30548  * Fork - LGPL
30549  * <script type="text/javascript">
30550  */
30551  
30552
30553
30554 /**
30555  * @class Roo.SplitLayoutRegion
30556  * @extends Roo.LayoutRegion
30557  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30558  */
30559 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30560     this.cursor = cursor;
30561     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30562 };
30563
30564 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30565     splitTip : "Drag to resize.",
30566     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30567     useSplitTips : false,
30568
30569     applyConfig : function(config){
30570         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30571         if(config.split){
30572             if(!this.split){
30573                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30574                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30575                 /** The SplitBar for this region 
30576                 * @type Roo.SplitBar */
30577                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30578                 this.split.on("moved", this.onSplitMove, this);
30579                 this.split.useShim = config.useShim === true;
30580                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30581                 if(this.useSplitTips){
30582                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30583                 }
30584                 if(config.collapsible){
30585                     this.split.el.on("dblclick", this.collapse,  this);
30586                 }
30587             }
30588             if(typeof config.minSize != "undefined"){
30589                 this.split.minSize = config.minSize;
30590             }
30591             if(typeof config.maxSize != "undefined"){
30592                 this.split.maxSize = config.maxSize;
30593             }
30594             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30595                 this.hideSplitter();
30596             }
30597         }
30598     },
30599
30600     getHMaxSize : function(){
30601          var cmax = this.config.maxSize || 10000;
30602          var center = this.mgr.getRegion("center");
30603          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30604     },
30605
30606     getVMaxSize : function(){
30607          var cmax = this.config.maxSize || 10000;
30608          var center = this.mgr.getRegion("center");
30609          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30610     },
30611
30612     onSplitMove : function(split, newSize){
30613         this.fireEvent("resized", this, newSize);
30614     },
30615     
30616     /** 
30617      * Returns the {@link Roo.SplitBar} for this region.
30618      * @return {Roo.SplitBar}
30619      */
30620     getSplitBar : function(){
30621         return this.split;
30622     },
30623     
30624     hide : function(){
30625         this.hideSplitter();
30626         Roo.SplitLayoutRegion.superclass.hide.call(this);
30627     },
30628
30629     hideSplitter : function(){
30630         if(this.split){
30631             this.split.el.setLocation(-2000,-2000);
30632             this.split.el.hide();
30633         }
30634     },
30635
30636     show : function(){
30637         if(this.split){
30638             this.split.el.show();
30639         }
30640         Roo.SplitLayoutRegion.superclass.show.call(this);
30641     },
30642     
30643     beforeSlide: function(){
30644         if(Roo.isGecko){// firefox overflow auto bug workaround
30645             this.bodyEl.clip();
30646             if(this.tabs) this.tabs.bodyEl.clip();
30647             if(this.activePanel){
30648                 this.activePanel.getEl().clip();
30649                 
30650                 if(this.activePanel.beforeSlide){
30651                     this.activePanel.beforeSlide();
30652                 }
30653             }
30654         }
30655     },
30656     
30657     afterSlide : function(){
30658         if(Roo.isGecko){// firefox overflow auto bug workaround
30659             this.bodyEl.unclip();
30660             if(this.tabs) this.tabs.bodyEl.unclip();
30661             if(this.activePanel){
30662                 this.activePanel.getEl().unclip();
30663                 if(this.activePanel.afterSlide){
30664                     this.activePanel.afterSlide();
30665                 }
30666             }
30667         }
30668     },
30669
30670     initAutoHide : function(){
30671         if(this.autoHide !== false){
30672             if(!this.autoHideHd){
30673                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30674                 this.autoHideHd = {
30675                     "mouseout": function(e){
30676                         if(!e.within(this.el, true)){
30677                             st.delay(500);
30678                         }
30679                     },
30680                     "mouseover" : function(e){
30681                         st.cancel();
30682                     },
30683                     scope : this
30684                 };
30685             }
30686             this.el.on(this.autoHideHd);
30687         }
30688     },
30689
30690     clearAutoHide : function(){
30691         if(this.autoHide !== false){
30692             this.el.un("mouseout", this.autoHideHd.mouseout);
30693             this.el.un("mouseover", this.autoHideHd.mouseover);
30694         }
30695     },
30696
30697     clearMonitor : function(){
30698         Roo.get(document).un("click", this.slideInIf, this);
30699     },
30700
30701     // these names are backwards but not changed for compat
30702     slideOut : function(){
30703         if(this.isSlid || this.el.hasActiveFx()){
30704             return;
30705         }
30706         this.isSlid = true;
30707         if(this.collapseBtn){
30708             this.collapseBtn.hide();
30709         }
30710         this.closeBtnState = this.closeBtn.getStyle('display');
30711         this.closeBtn.hide();
30712         if(this.stickBtn){
30713             this.stickBtn.show();
30714         }
30715         this.el.show();
30716         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30717         this.beforeSlide();
30718         this.el.setStyle("z-index", 10001);
30719         this.el.slideIn(this.getSlideAnchor(), {
30720             callback: function(){
30721                 this.afterSlide();
30722                 this.initAutoHide();
30723                 Roo.get(document).on("click", this.slideInIf, this);
30724                 this.fireEvent("slideshow", this);
30725             },
30726             scope: this,
30727             block: true
30728         });
30729     },
30730
30731     afterSlideIn : function(){
30732         this.clearAutoHide();
30733         this.isSlid = false;
30734         this.clearMonitor();
30735         this.el.setStyle("z-index", "");
30736         if(this.collapseBtn){
30737             this.collapseBtn.show();
30738         }
30739         this.closeBtn.setStyle('display', this.closeBtnState);
30740         if(this.stickBtn){
30741             this.stickBtn.hide();
30742         }
30743         this.fireEvent("slidehide", this);
30744     },
30745
30746     slideIn : function(cb){
30747         if(!this.isSlid || this.el.hasActiveFx()){
30748             Roo.callback(cb);
30749             return;
30750         }
30751         this.isSlid = false;
30752         this.beforeSlide();
30753         this.el.slideOut(this.getSlideAnchor(), {
30754             callback: function(){
30755                 this.el.setLeftTop(-10000, -10000);
30756                 this.afterSlide();
30757                 this.afterSlideIn();
30758                 Roo.callback(cb);
30759             },
30760             scope: this,
30761             block: true
30762         });
30763     },
30764     
30765     slideInIf : function(e){
30766         if(!e.within(this.el)){
30767             this.slideIn();
30768         }
30769     },
30770
30771     animateCollapse : function(){
30772         this.beforeSlide();
30773         this.el.setStyle("z-index", 20000);
30774         var anchor = this.getSlideAnchor();
30775         this.el.slideOut(anchor, {
30776             callback : function(){
30777                 this.el.setStyle("z-index", "");
30778                 this.collapsedEl.slideIn(anchor, {duration:.3});
30779                 this.afterSlide();
30780                 this.el.setLocation(-10000,-10000);
30781                 this.el.hide();
30782                 this.fireEvent("collapsed", this);
30783             },
30784             scope: this,
30785             block: true
30786         });
30787     },
30788
30789     animateExpand : function(){
30790         this.beforeSlide();
30791         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30792         this.el.setStyle("z-index", 20000);
30793         this.collapsedEl.hide({
30794             duration:.1
30795         });
30796         this.el.slideIn(this.getSlideAnchor(), {
30797             callback : function(){
30798                 this.el.setStyle("z-index", "");
30799                 this.afterSlide();
30800                 if(this.split){
30801                     this.split.el.show();
30802                 }
30803                 this.fireEvent("invalidated", this);
30804                 this.fireEvent("expanded", this);
30805             },
30806             scope: this,
30807             block: true
30808         });
30809     },
30810
30811     anchors : {
30812         "west" : "left",
30813         "east" : "right",
30814         "north" : "top",
30815         "south" : "bottom"
30816     },
30817
30818     sanchors : {
30819         "west" : "l",
30820         "east" : "r",
30821         "north" : "t",
30822         "south" : "b"
30823     },
30824
30825     canchors : {
30826         "west" : "tl-tr",
30827         "east" : "tr-tl",
30828         "north" : "tl-bl",
30829         "south" : "bl-tl"
30830     },
30831
30832     getAnchor : function(){
30833         return this.anchors[this.position];
30834     },
30835
30836     getCollapseAnchor : function(){
30837         return this.canchors[this.position];
30838     },
30839
30840     getSlideAnchor : function(){
30841         return this.sanchors[this.position];
30842     },
30843
30844     getAlignAdj : function(){
30845         var cm = this.cmargins;
30846         switch(this.position){
30847             case "west":
30848                 return [0, 0];
30849             break;
30850             case "east":
30851                 return [0, 0];
30852             break;
30853             case "north":
30854                 return [0, 0];
30855             break;
30856             case "south":
30857                 return [0, 0];
30858             break;
30859         }
30860     },
30861
30862     getExpandAdj : function(){
30863         var c = this.collapsedEl, cm = this.cmargins;
30864         switch(this.position){
30865             case "west":
30866                 return [-(cm.right+c.getWidth()+cm.left), 0];
30867             break;
30868             case "east":
30869                 return [cm.right+c.getWidth()+cm.left, 0];
30870             break;
30871             case "north":
30872                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30873             break;
30874             case "south":
30875                 return [0, cm.top+cm.bottom+c.getHeight()];
30876             break;
30877         }
30878     }
30879 });/*
30880  * Based on:
30881  * Ext JS Library 1.1.1
30882  * Copyright(c) 2006-2007, Ext JS, LLC.
30883  *
30884  * Originally Released Under LGPL - original licence link has changed is not relivant.
30885  *
30886  * Fork - LGPL
30887  * <script type="text/javascript">
30888  */
30889 /*
30890  * These classes are private internal classes
30891  */
30892 Roo.CenterLayoutRegion = function(mgr, config){
30893     Roo.LayoutRegion.call(this, mgr, config, "center");
30894     this.visible = true;
30895     this.minWidth = config.minWidth || 20;
30896     this.minHeight = config.minHeight || 20;
30897 };
30898
30899 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30900     hide : function(){
30901         // center panel can't be hidden
30902     },
30903     
30904     show : function(){
30905         // center panel can't be hidden
30906     },
30907     
30908     getMinWidth: function(){
30909         return this.minWidth;
30910     },
30911     
30912     getMinHeight: function(){
30913         return this.minHeight;
30914     }
30915 });
30916
30917
30918 Roo.NorthLayoutRegion = function(mgr, config){
30919     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30920     if(this.split){
30921         this.split.placement = Roo.SplitBar.TOP;
30922         this.split.orientation = Roo.SplitBar.VERTICAL;
30923         this.split.el.addClass("x-layout-split-v");
30924     }
30925     var size = config.initialSize || config.height;
30926     if(typeof size != "undefined"){
30927         this.el.setHeight(size);
30928     }
30929 };
30930 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30931     orientation: Roo.SplitBar.VERTICAL,
30932     getBox : function(){
30933         if(this.collapsed){
30934             return this.collapsedEl.getBox();
30935         }
30936         var box = this.el.getBox();
30937         if(this.split){
30938             box.height += this.split.el.getHeight();
30939         }
30940         return box;
30941     },
30942     
30943     updateBox : function(box){
30944         if(this.split && !this.collapsed){
30945             box.height -= this.split.el.getHeight();
30946             this.split.el.setLeft(box.x);
30947             this.split.el.setTop(box.y+box.height);
30948             this.split.el.setWidth(box.width);
30949         }
30950         if(this.collapsed){
30951             this.updateBody(box.width, null);
30952         }
30953         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30954     }
30955 });
30956
30957 Roo.SouthLayoutRegion = function(mgr, config){
30958     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30959     if(this.split){
30960         this.split.placement = Roo.SplitBar.BOTTOM;
30961         this.split.orientation = Roo.SplitBar.VERTICAL;
30962         this.split.el.addClass("x-layout-split-v");
30963     }
30964     var size = config.initialSize || config.height;
30965     if(typeof size != "undefined"){
30966         this.el.setHeight(size);
30967     }
30968 };
30969 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30970     orientation: Roo.SplitBar.VERTICAL,
30971     getBox : function(){
30972         if(this.collapsed){
30973             return this.collapsedEl.getBox();
30974         }
30975         var box = this.el.getBox();
30976         if(this.split){
30977             var sh = this.split.el.getHeight();
30978             box.height += sh;
30979             box.y -= sh;
30980         }
30981         return box;
30982     },
30983     
30984     updateBox : function(box){
30985         if(this.split && !this.collapsed){
30986             var sh = this.split.el.getHeight();
30987             box.height -= sh;
30988             box.y += sh;
30989             this.split.el.setLeft(box.x);
30990             this.split.el.setTop(box.y-sh);
30991             this.split.el.setWidth(box.width);
30992         }
30993         if(this.collapsed){
30994             this.updateBody(box.width, null);
30995         }
30996         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30997     }
30998 });
30999
31000 Roo.EastLayoutRegion = function(mgr, config){
31001     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31002     if(this.split){
31003         this.split.placement = Roo.SplitBar.RIGHT;
31004         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31005         this.split.el.addClass("x-layout-split-h");
31006     }
31007     var size = config.initialSize || config.width;
31008     if(typeof size != "undefined"){
31009         this.el.setWidth(size);
31010     }
31011 };
31012 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31013     orientation: Roo.SplitBar.HORIZONTAL,
31014     getBox : function(){
31015         if(this.collapsed){
31016             return this.collapsedEl.getBox();
31017         }
31018         var box = this.el.getBox();
31019         if(this.split){
31020             var sw = this.split.el.getWidth();
31021             box.width += sw;
31022             box.x -= sw;
31023         }
31024         return box;
31025     },
31026
31027     updateBox : function(box){
31028         if(this.split && !this.collapsed){
31029             var sw = this.split.el.getWidth();
31030             box.width -= sw;
31031             this.split.el.setLeft(box.x);
31032             this.split.el.setTop(box.y);
31033             this.split.el.setHeight(box.height);
31034             box.x += sw;
31035         }
31036         if(this.collapsed){
31037             this.updateBody(null, box.height);
31038         }
31039         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31040     }
31041 });
31042
31043 Roo.WestLayoutRegion = function(mgr, config){
31044     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31045     if(this.split){
31046         this.split.placement = Roo.SplitBar.LEFT;
31047         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31048         this.split.el.addClass("x-layout-split-h");
31049     }
31050     var size = config.initialSize || config.width;
31051     if(typeof size != "undefined"){
31052         this.el.setWidth(size);
31053     }
31054 };
31055 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31056     orientation: Roo.SplitBar.HORIZONTAL,
31057     getBox : function(){
31058         if(this.collapsed){
31059             return this.collapsedEl.getBox();
31060         }
31061         var box = this.el.getBox();
31062         if(this.split){
31063             box.width += this.split.el.getWidth();
31064         }
31065         return box;
31066     },
31067     
31068     updateBox : function(box){
31069         if(this.split && !this.collapsed){
31070             var sw = this.split.el.getWidth();
31071             box.width -= sw;
31072             this.split.el.setLeft(box.x+box.width);
31073             this.split.el.setTop(box.y);
31074             this.split.el.setHeight(box.height);
31075         }
31076         if(this.collapsed){
31077             this.updateBody(null, box.height);
31078         }
31079         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31080     }
31081 });
31082 /*
31083  * Based on:
31084  * Ext JS Library 1.1.1
31085  * Copyright(c) 2006-2007, Ext JS, LLC.
31086  *
31087  * Originally Released Under LGPL - original licence link has changed is not relivant.
31088  *
31089  * Fork - LGPL
31090  * <script type="text/javascript">
31091  */
31092  
31093  
31094 /*
31095  * Private internal class for reading and applying state
31096  */
31097 Roo.LayoutStateManager = function(layout){
31098      // default empty state
31099      this.state = {
31100         north: {},
31101         south: {},
31102         east: {},
31103         west: {}       
31104     };
31105 };
31106
31107 Roo.LayoutStateManager.prototype = {
31108     init : function(layout, provider){
31109         this.provider = provider;
31110         var state = provider.get(layout.id+"-layout-state");
31111         if(state){
31112             var wasUpdating = layout.isUpdating();
31113             if(!wasUpdating){
31114                 layout.beginUpdate();
31115             }
31116             for(var key in state){
31117                 if(typeof state[key] != "function"){
31118                     var rstate = state[key];
31119                     var r = layout.getRegion(key);
31120                     if(r && rstate){
31121                         if(rstate.size){
31122                             r.resizeTo(rstate.size);
31123                         }
31124                         if(rstate.collapsed == true){
31125                             r.collapse(true);
31126                         }else{
31127                             r.expand(null, true);
31128                         }
31129                     }
31130                 }
31131             }
31132             if(!wasUpdating){
31133                 layout.endUpdate();
31134             }
31135             this.state = state; 
31136         }
31137         this.layout = layout;
31138         layout.on("regionresized", this.onRegionResized, this);
31139         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31140         layout.on("regionexpanded", this.onRegionExpanded, this);
31141     },
31142     
31143     storeState : function(){
31144         this.provider.set(this.layout.id+"-layout-state", this.state);
31145     },
31146     
31147     onRegionResized : function(region, newSize){
31148         this.state[region.getPosition()].size = newSize;
31149         this.storeState();
31150     },
31151     
31152     onRegionCollapsed : function(region){
31153         this.state[region.getPosition()].collapsed = true;
31154         this.storeState();
31155     },
31156     
31157     onRegionExpanded : function(region){
31158         this.state[region.getPosition()].collapsed = false;
31159         this.storeState();
31160     }
31161 };/*
31162  * Based on:
31163  * Ext JS Library 1.1.1
31164  * Copyright(c) 2006-2007, Ext JS, LLC.
31165  *
31166  * Originally Released Under LGPL - original licence link has changed is not relivant.
31167  *
31168  * Fork - LGPL
31169  * <script type="text/javascript">
31170  */
31171 /**
31172  * @class Roo.ContentPanel
31173  * @extends Roo.util.Observable
31174  * A basic ContentPanel element.
31175  * @cfg {Boolean} fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31176  * @cfg {Boolean} fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31177  * @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
31178  * @cfg {Boolean} closable      True if the panel can be closed/removed
31179  * @cfg {Boolean} background    True if the panel should not be activated when it is added (defaults to false)
31180  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31181  * @cfg {Toolbar} toolbar       A toolbar for this panel
31182  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31183  * @cfg {String} title          The title for this panel
31184  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31185  * @cfg {String} url            Calls {@link #setUrl} with this value
31186  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31187  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31188  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31189  * @cfg {String} content        Raw content to fill content panel with (uses setContent on construction.)
31190
31191  * @constructor
31192  * Create a new ContentPanel.
31193  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31194  * @param {String/Object} config A string to set only the title or a config object
31195  * @param {String} content (optional) Set the HTML content for this panel
31196  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31197  */
31198 Roo.ContentPanel = function(el, config, content){
31199     
31200      
31201     /*
31202     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31203         config = el;
31204         el = Roo.id();
31205     }
31206     if (config && config.parentLayout) { 
31207         el = config.parentLayout.el.createChild(); 
31208     }
31209     */
31210     if(el.autoCreate){ // xtype is available if this is called from factory
31211         config = el;
31212         el = Roo.id();
31213     }
31214     this.el = Roo.get(el);
31215     if(!this.el && config && config.autoCreate){
31216         if(typeof config.autoCreate == "object"){
31217             if(!config.autoCreate.id){
31218                 config.autoCreate.id = config.id||el;
31219             }
31220             this.el = Roo.DomHelper.append(document.body,
31221                         config.autoCreate, true);
31222         }else{
31223             this.el = Roo.DomHelper.append(document.body,
31224                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31225         }
31226     }
31227     this.closable = false;
31228     this.loaded = false;
31229     this.active = false;
31230     if(typeof config == "string"){
31231         this.title = config;
31232     }else{
31233         Roo.apply(this, config);
31234     }
31235     
31236     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31237         this.wrapEl = this.el.wrap();    
31238         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
31239         
31240     }
31241     
31242     
31243     
31244     if(this.resizeEl){
31245         this.resizeEl = Roo.get(this.resizeEl, true);
31246     }else{
31247         this.resizeEl = this.el;
31248     }
31249     this.addEvents({
31250         /**
31251          * @event activate
31252          * Fires when this panel is activated. 
31253          * @param {Roo.ContentPanel} this
31254          */
31255         "activate" : true,
31256         /**
31257          * @event deactivate
31258          * Fires when this panel is activated. 
31259          * @param {Roo.ContentPanel} this
31260          */
31261         "deactivate" : true,
31262
31263         /**
31264          * @event resize
31265          * Fires when this panel is resized if fitToFrame is true.
31266          * @param {Roo.ContentPanel} this
31267          * @param {Number} width The width after any component adjustments
31268          * @param {Number} height The height after any component adjustments
31269          */
31270         "resize" : true
31271     });
31272     if(this.autoScroll){
31273         this.resizeEl.setStyle("overflow", "auto");
31274     } else {
31275         // fix randome scrolling
31276         this.el.on('scroll', function() {
31277             Roo.log('fix random scolling');
31278             this.scrollTo('top',0); 
31279         });
31280     }
31281     content = content || this.content;
31282     if(content){
31283         this.setContent(content);
31284     }
31285     if(config && config.url){
31286         this.setUrl(this.url, this.params, this.loadOnce);
31287     }
31288     
31289     
31290     
31291     Roo.ContentPanel.superclass.constructor.call(this);
31292 };
31293
31294 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31295     tabTip:'',
31296     setRegion : function(region){
31297         this.region = region;
31298         if(region){
31299            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31300         }else{
31301            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31302         } 
31303     },
31304     
31305     /**
31306      * Returns the toolbar for this Panel if one was configured. 
31307      * @return {Roo.Toolbar} 
31308      */
31309     getToolbar : function(){
31310         return this.toolbar;
31311     },
31312     
31313     setActiveState : function(active){
31314         this.active = active;
31315         if(!active){
31316             this.fireEvent("deactivate", this);
31317         }else{
31318             this.fireEvent("activate", this);
31319         }
31320     },
31321     /**
31322      * Updates this panel's element
31323      * @param {String} content The new content
31324      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31325     */
31326     setContent : function(content, loadScripts){
31327         this.el.update(content, loadScripts);
31328     },
31329
31330     ignoreResize : function(w, h){
31331         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31332             return true;
31333         }else{
31334             this.lastSize = {width: w, height: h};
31335             return false;
31336         }
31337     },
31338     /**
31339      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31340      * @return {Roo.UpdateManager} The UpdateManager
31341      */
31342     getUpdateManager : function(){
31343         return this.el.getUpdateManager();
31344     },
31345      /**
31346      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31347      * @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:
31348 <pre><code>
31349 panel.load({
31350     url: "your-url.php",
31351     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31352     callback: yourFunction,
31353     scope: yourObject, //(optional scope)
31354     discardUrl: false,
31355     nocache: false,
31356     text: "Loading...",
31357     timeout: 30,
31358     scripts: false
31359 });
31360 </code></pre>
31361      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31362      * 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.
31363      * @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}
31364      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31365      * @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.
31366      * @return {Roo.ContentPanel} this
31367      */
31368     load : function(){
31369         var um = this.el.getUpdateManager();
31370         um.update.apply(um, arguments);
31371         return this;
31372     },
31373
31374
31375     /**
31376      * 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.
31377      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31378      * @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)
31379      * @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)
31380      * @return {Roo.UpdateManager} The UpdateManager
31381      */
31382     setUrl : function(url, params, loadOnce){
31383         if(this.refreshDelegate){
31384             this.removeListener("activate", this.refreshDelegate);
31385         }
31386         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31387         this.on("activate", this.refreshDelegate);
31388         return this.el.getUpdateManager();
31389     },
31390     
31391     _handleRefresh : function(url, params, loadOnce){
31392         if(!loadOnce || !this.loaded){
31393             var updater = this.el.getUpdateManager();
31394             updater.update(url, params, this._setLoaded.createDelegate(this));
31395         }
31396     },
31397     
31398     _setLoaded : function(){
31399         this.loaded = true;
31400     }, 
31401     
31402     /**
31403      * Returns this panel's id
31404      * @return {String} 
31405      */
31406     getId : function(){
31407         return this.el.id;
31408     },
31409     
31410     /** 
31411      * Returns this panel's element - used by regiosn to add.
31412      * @return {Roo.Element} 
31413      */
31414     getEl : function(){
31415         return this.wrapEl || this.el;
31416     },
31417     
31418     adjustForComponents : function(width, height){
31419         if(this.resizeEl != this.el){
31420             width -= this.el.getFrameWidth('lr');
31421             height -= this.el.getFrameWidth('tb');
31422         }
31423         if(this.toolbar){
31424             var te = this.toolbar.getEl();
31425             height -= te.getHeight();
31426             te.setWidth(width);
31427         }
31428         if(this.adjustments){
31429             width += this.adjustments[0];
31430             height += this.adjustments[1];
31431         }
31432         return {"width": width, "height": height};
31433     },
31434     
31435     setSize : function(width, height){
31436         if(this.fitToFrame && !this.ignoreResize(width, height)){
31437             if(this.fitContainer && this.resizeEl != this.el){
31438                 this.el.setSize(width, height);
31439             }
31440             var size = this.adjustForComponents(width, height);
31441             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31442             this.fireEvent('resize', this, size.width, size.height);
31443         }
31444     },
31445     
31446     /**
31447      * Returns this panel's title
31448      * @return {String} 
31449      */
31450     getTitle : function(){
31451         return this.title;
31452     },
31453     
31454     /**
31455      * Set this panel's title
31456      * @param {String} title
31457      */
31458     setTitle : function(title){
31459         this.title = title;
31460         if(this.region){
31461             this.region.updatePanelTitle(this, title);
31462         }
31463     },
31464     
31465     /**
31466      * Returns true is this panel was configured to be closable
31467      * @return {Boolean} 
31468      */
31469     isClosable : function(){
31470         return this.closable;
31471     },
31472     
31473     beforeSlide : function(){
31474         this.el.clip();
31475         this.resizeEl.clip();
31476     },
31477     
31478     afterSlide : function(){
31479         this.el.unclip();
31480         this.resizeEl.unclip();
31481     },
31482     
31483     /**
31484      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31485      *   Will fail silently if the {@link #setUrl} method has not been called.
31486      *   This does not activate the panel, just updates its content.
31487      */
31488     refresh : function(){
31489         if(this.refreshDelegate){
31490            this.loaded = false;
31491            this.refreshDelegate();
31492         }
31493     },
31494     
31495     /**
31496      * Destroys this panel
31497      */
31498     destroy : function(){
31499         this.el.removeAllListeners();
31500         var tempEl = document.createElement("span");
31501         tempEl.appendChild(this.el.dom);
31502         tempEl.innerHTML = "";
31503         this.el.remove();
31504         this.el = null;
31505     },
31506     
31507       /**
31508      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31509      * <pre><code>
31510
31511 layout.addxtype({
31512        xtype : 'Form',
31513        items: [ .... ]
31514    }
31515 );
31516
31517 </code></pre>
31518      * @param {Object} cfg Xtype definition of item to add.
31519      */
31520     
31521     addxtype : function(cfg) {
31522         // add form..
31523         if (cfg.xtype.match(/^Form$/)) {
31524             var el = this.el.createChild();
31525
31526             this.form = new  Roo.form.Form(cfg);
31527             
31528             
31529             if ( this.form.allItems.length) this.form.render(el.dom);
31530             return this.form;
31531         }
31532         
31533         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31534             // views..
31535             cfg.el = this.el.appendChild(document.createElement("div"));
31536             // factory?
31537             var ret = new Roo[cfg.xtype](cfg);
31538             ret.render(false, ''); // render blank..
31539             return ret;
31540             
31541         }
31542         return false;
31543         
31544     }
31545 });
31546
31547 /**
31548  * @class Roo.GridPanel
31549  * @extends Roo.ContentPanel
31550  * @constructor
31551  * Create a new GridPanel.
31552  * @param {Roo.grid.Grid} grid The grid for this panel
31553  * @param {String/Object} config A string to set only the panel's title, or a config object
31554  */
31555 Roo.GridPanel = function(grid, config){
31556     
31557   
31558     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31559         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31560         
31561     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31562     
31563     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31564     
31565     if(this.toolbar){
31566         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31567     }
31568     // xtype created footer. - not sure if will work as we normally have to render first..
31569     if (this.footer && !this.footer.el && this.footer.xtype) {
31570         
31571         this.footer.container = this.grid.getView().getFooterPanel(true);
31572         this.footer.dataSource = this.grid.dataSource;
31573         this.footer = Roo.factory(this.footer, Roo);
31574         
31575     }
31576     
31577     grid.monitorWindowResize = false; // turn off autosizing
31578     grid.autoHeight = false;
31579     grid.autoWidth = false;
31580     this.grid = grid;
31581     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31582 };
31583
31584 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31585     getId : function(){
31586         return this.grid.id;
31587     },
31588     
31589     /**
31590      * Returns the grid for this panel
31591      * @return {Roo.grid.Grid} 
31592      */
31593     getGrid : function(){
31594         return this.grid;    
31595     },
31596     
31597     setSize : function(width, height){
31598         if(!this.ignoreResize(width, height)){
31599             var grid = this.grid;
31600             var size = this.adjustForComponents(width, height);
31601             grid.getGridEl().setSize(size.width, size.height);
31602             grid.autoSize();
31603         }
31604     },
31605     
31606     beforeSlide : function(){
31607         this.grid.getView().scroller.clip();
31608     },
31609     
31610     afterSlide : function(){
31611         this.grid.getView().scroller.unclip();
31612     },
31613     
31614     destroy : function(){
31615         this.grid.destroy();
31616         delete this.grid;
31617         Roo.GridPanel.superclass.destroy.call(this); 
31618     }
31619 });
31620
31621
31622 /**
31623  * @class Roo.NestedLayoutPanel
31624  * @extends Roo.ContentPanel
31625  * @constructor
31626  * Create a new NestedLayoutPanel.
31627  * 
31628  * 
31629  * @param {Roo.BorderLayout} layout The layout for this panel
31630  * @param {String/Object} config A string to set only the title or a config object
31631  */
31632 Roo.NestedLayoutPanel = function(layout, config)
31633 {
31634     // construct with only one argument..
31635     /* FIXME - implement nicer consturctors
31636     if (layout.layout) {
31637         config = layout;
31638         layout = config.layout;
31639         delete config.layout;
31640     }
31641     if (layout.xtype && !layout.getEl) {
31642         // then layout needs constructing..
31643         layout = Roo.factory(layout, Roo);
31644     }
31645     */
31646     
31647     
31648     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31649     
31650     layout.monitorWindowResize = false; // turn off autosizing
31651     this.layout = layout;
31652     this.layout.getEl().addClass("x-layout-nested-layout");
31653     
31654     
31655     
31656     
31657 };
31658
31659 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31660
31661     setSize : function(width, height){
31662         if(!this.ignoreResize(width, height)){
31663             var size = this.adjustForComponents(width, height);
31664             var el = this.layout.getEl();
31665             el.setSize(size.width, size.height);
31666             var touch = el.dom.offsetWidth;
31667             this.layout.layout();
31668             // ie requires a double layout on the first pass
31669             if(Roo.isIE && !this.initialized){
31670                 this.initialized = true;
31671                 this.layout.layout();
31672             }
31673         }
31674     },
31675     
31676     // activate all subpanels if not currently active..
31677     
31678     setActiveState : function(active){
31679         this.active = active;
31680         if(!active){
31681             this.fireEvent("deactivate", this);
31682             return;
31683         }
31684         
31685         this.fireEvent("activate", this);
31686         // not sure if this should happen before or after..
31687         if (!this.layout) {
31688             return; // should not happen..
31689         }
31690         var reg = false;
31691         for (var r in this.layout.regions) {
31692             reg = this.layout.getRegion(r);
31693             if (reg.getActivePanel()) {
31694                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31695                 reg.setActivePanel(reg.getActivePanel());
31696                 continue;
31697             }
31698             if (!reg.panels.length) {
31699                 continue;
31700             }
31701             reg.showPanel(reg.getPanel(0));
31702         }
31703         
31704         
31705         
31706         
31707     },
31708     
31709     /**
31710      * Returns the nested BorderLayout for this panel
31711      * @return {Roo.BorderLayout} 
31712      */
31713     getLayout : function(){
31714         return this.layout;
31715     },
31716     
31717      /**
31718      * Adds a xtype elements to the layout of the nested panel
31719      * <pre><code>
31720
31721 panel.addxtype({
31722        xtype : 'ContentPanel',
31723        region: 'west',
31724        items: [ .... ]
31725    }
31726 );
31727
31728 panel.addxtype({
31729         xtype : 'NestedLayoutPanel',
31730         region: 'west',
31731         layout: {
31732            center: { },
31733            west: { }   
31734         },
31735         items : [ ... list of content panels or nested layout panels.. ]
31736    }
31737 );
31738 </code></pre>
31739      * @param {Object} cfg Xtype definition of item to add.
31740      */
31741     addxtype : function(cfg) {
31742         return this.layout.addxtype(cfg);
31743     
31744     }
31745 });
31746
31747 Roo.ScrollPanel = function(el, config, content){
31748     config = config || {};
31749     config.fitToFrame = true;
31750     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31751     
31752     this.el.dom.style.overflow = "hidden";
31753     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31754     this.el.removeClass("x-layout-inactive-content");
31755     this.el.on("mousewheel", this.onWheel, this);
31756
31757     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31758     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31759     up.unselectable(); down.unselectable();
31760     up.on("click", this.scrollUp, this);
31761     down.on("click", this.scrollDown, this);
31762     up.addClassOnOver("x-scroller-btn-over");
31763     down.addClassOnOver("x-scroller-btn-over");
31764     up.addClassOnClick("x-scroller-btn-click");
31765     down.addClassOnClick("x-scroller-btn-click");
31766     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31767
31768     this.resizeEl = this.el;
31769     this.el = wrap; this.up = up; this.down = down;
31770 };
31771
31772 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31773     increment : 100,
31774     wheelIncrement : 5,
31775     scrollUp : function(){
31776         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31777     },
31778
31779     scrollDown : function(){
31780         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31781     },
31782
31783     afterScroll : function(){
31784         var el = this.resizeEl;
31785         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31786         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31787         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31788     },
31789
31790     setSize : function(){
31791         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31792         this.afterScroll();
31793     },
31794
31795     onWheel : function(e){
31796         var d = e.getWheelDelta();
31797         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31798         this.afterScroll();
31799         e.stopEvent();
31800     },
31801
31802     setContent : function(content, loadScripts){
31803         this.resizeEl.update(content, loadScripts);
31804     }
31805
31806 });
31807
31808
31809
31810
31811
31812
31813
31814
31815
31816 /**
31817  * @class Roo.TreePanel
31818  * @extends Roo.ContentPanel
31819  * @constructor
31820  * Create a new TreePanel. - defaults to fit/scoll contents.
31821  * @param {String/Object} config A string to set only the panel's title, or a config object
31822  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31823  */
31824 Roo.TreePanel = function(config){
31825     var el = config.el;
31826     var tree = config.tree;
31827     delete config.tree; 
31828     delete config.el; // hopefull!
31829     
31830     // wrapper for IE7 strict & safari scroll issue
31831     
31832     var treeEl = el.createChild();
31833     config.resizeEl = treeEl;
31834     
31835     
31836     
31837     Roo.TreePanel.superclass.constructor.call(this, el, config);
31838  
31839  
31840     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31841     //console.log(tree);
31842     this.on('activate', function()
31843     {
31844         if (this.tree.rendered) {
31845             return;
31846         }
31847         //console.log('render tree');
31848         this.tree.render();
31849     });
31850     
31851     this.on('resize',  function (cp, w, h) {
31852             this.tree.innerCt.setWidth(w);
31853             this.tree.innerCt.setHeight(h);
31854             this.tree.innerCt.setStyle('overflow-y', 'auto');
31855     });
31856
31857         
31858     
31859 };
31860
31861 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31862     fitToFrame : true,
31863     autoScroll : true
31864 });
31865
31866
31867
31868
31869
31870
31871
31872
31873
31874
31875
31876 /*
31877  * Based on:
31878  * Ext JS Library 1.1.1
31879  * Copyright(c) 2006-2007, Ext JS, LLC.
31880  *
31881  * Originally Released Under LGPL - original licence link has changed is not relivant.
31882  *
31883  * Fork - LGPL
31884  * <script type="text/javascript">
31885  */
31886  
31887
31888 /**
31889  * @class Roo.ReaderLayout
31890  * @extends Roo.BorderLayout
31891  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31892  * center region containing two nested regions (a top one for a list view and one for item preview below),
31893  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31894  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31895  * expedites the setup of the overall layout and regions for this common application style.
31896  * Example:
31897  <pre><code>
31898 var reader = new Roo.ReaderLayout();
31899 var CP = Roo.ContentPanel;  // shortcut for adding
31900
31901 reader.beginUpdate();
31902 reader.add("north", new CP("north", "North"));
31903 reader.add("west", new CP("west", {title: "West"}));
31904 reader.add("east", new CP("east", {title: "East"}));
31905
31906 reader.regions.listView.add(new CP("listView", "List"));
31907 reader.regions.preview.add(new CP("preview", "Preview"));
31908 reader.endUpdate();
31909 </code></pre>
31910 * @constructor
31911 * Create a new ReaderLayout
31912 * @param {Object} config Configuration options
31913 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31914 * document.body if omitted)
31915 */
31916 Roo.ReaderLayout = function(config, renderTo){
31917     var c = config || {size:{}};
31918     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31919         north: c.north !== false ? Roo.apply({
31920             split:false,
31921             initialSize: 32,
31922             titlebar: false
31923         }, c.north) : false,
31924         west: c.west !== false ? Roo.apply({
31925             split:true,
31926             initialSize: 200,
31927             minSize: 175,
31928             maxSize: 400,
31929             titlebar: true,
31930             collapsible: true,
31931             animate: true,
31932             margins:{left:5,right:0,bottom:5,top:5},
31933             cmargins:{left:5,right:5,bottom:5,top:5}
31934         }, c.west) : false,
31935         east: c.east !== false ? Roo.apply({
31936             split:true,
31937             initialSize: 200,
31938             minSize: 175,
31939             maxSize: 400,
31940             titlebar: true,
31941             collapsible: true,
31942             animate: true,
31943             margins:{left:0,right:5,bottom:5,top:5},
31944             cmargins:{left:5,right:5,bottom:5,top:5}
31945         }, c.east) : false,
31946         center: Roo.apply({
31947             tabPosition: 'top',
31948             autoScroll:false,
31949             closeOnTab: true,
31950             titlebar:false,
31951             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31952         }, c.center)
31953     });
31954
31955     this.el.addClass('x-reader');
31956
31957     this.beginUpdate();
31958
31959     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31960         south: c.preview !== false ? Roo.apply({
31961             split:true,
31962             initialSize: 200,
31963             minSize: 100,
31964             autoScroll:true,
31965             collapsible:true,
31966             titlebar: true,
31967             cmargins:{top:5,left:0, right:0, bottom:0}
31968         }, c.preview) : false,
31969         center: Roo.apply({
31970             autoScroll:false,
31971             titlebar:false,
31972             minHeight:200
31973         }, c.listView)
31974     });
31975     this.add('center', new Roo.NestedLayoutPanel(inner,
31976             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31977
31978     this.endUpdate();
31979
31980     this.regions.preview = inner.getRegion('south');
31981     this.regions.listView = inner.getRegion('center');
31982 };
31983
31984 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31985  * Based on:
31986  * Ext JS Library 1.1.1
31987  * Copyright(c) 2006-2007, Ext JS, LLC.
31988  *
31989  * Originally Released Under LGPL - original licence link has changed is not relivant.
31990  *
31991  * Fork - LGPL
31992  * <script type="text/javascript">
31993  */
31994  
31995 /**
31996  * @class Roo.grid.Grid
31997  * @extends Roo.util.Observable
31998  * This class represents the primary interface of a component based grid control.
31999  * <br><br>Usage:<pre><code>
32000  var grid = new Roo.grid.Grid("my-container-id", {
32001      ds: myDataStore,
32002      cm: myColModel,
32003      selModel: mySelectionModel,
32004      autoSizeColumns: true,
32005      monitorWindowResize: false,
32006      trackMouseOver: true
32007  });
32008  // set any options
32009  grid.render();
32010  * </code></pre>
32011  * <b>Common Problems:</b><br/>
32012  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32013  * element will correct this<br/>
32014  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32015  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32016  * are unpredictable.<br/>
32017  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32018  * grid to calculate dimensions/offsets.<br/>
32019   * @constructor
32020  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32021  * The container MUST have some type of size defined for the grid to fill. The container will be
32022  * automatically set to position relative if it isn't already.
32023  * @param {Object} config A config object that sets properties on this grid.
32024  */
32025 Roo.grid.Grid = function(container, config){
32026         // initialize the container
32027         this.container = Roo.get(container);
32028         this.container.update("");
32029         this.container.setStyle("overflow", "hidden");
32030     this.container.addClass('x-grid-container');
32031
32032     this.id = this.container.id;
32033
32034     Roo.apply(this, config);
32035     // check and correct shorthanded configs
32036     if(this.ds){
32037         this.dataSource = this.ds;
32038         delete this.ds;
32039     }
32040     if(this.cm){
32041         this.colModel = this.cm;
32042         delete this.cm;
32043     }
32044     if(this.sm){
32045         this.selModel = this.sm;
32046         delete this.sm;
32047     }
32048
32049     if (this.selModel) {
32050         this.selModel = Roo.factory(this.selModel, Roo.grid);
32051         this.sm = this.selModel;
32052         this.sm.xmodule = this.xmodule || false;
32053     }
32054     if (typeof(this.colModel.config) == 'undefined') {
32055         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32056         this.cm = this.colModel;
32057         this.cm.xmodule = this.xmodule || false;
32058     }
32059     if (this.dataSource) {
32060         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32061         this.ds = this.dataSource;
32062         this.ds.xmodule = this.xmodule || false;
32063          
32064     }
32065     
32066     
32067     
32068     if(this.width){
32069         this.container.setWidth(this.width);
32070     }
32071
32072     if(this.height){
32073         this.container.setHeight(this.height);
32074     }
32075     /** @private */
32076         this.addEvents({
32077         // raw events
32078         /**
32079          * @event click
32080          * The raw click event for the entire grid.
32081          * @param {Roo.EventObject} e
32082          */
32083         "click" : true,
32084         /**
32085          * @event dblclick
32086          * The raw dblclick event for the entire grid.
32087          * @param {Roo.EventObject} e
32088          */
32089         "dblclick" : true,
32090         /**
32091          * @event contextmenu
32092          * The raw contextmenu event for the entire grid.
32093          * @param {Roo.EventObject} e
32094          */
32095         "contextmenu" : true,
32096         /**
32097          * @event mousedown
32098          * The raw mousedown event for the entire grid.
32099          * @param {Roo.EventObject} e
32100          */
32101         "mousedown" : true,
32102         /**
32103          * @event mouseup
32104          * The raw mouseup event for the entire grid.
32105          * @param {Roo.EventObject} e
32106          */
32107         "mouseup" : true,
32108         /**
32109          * @event mouseover
32110          * The raw mouseover event for the entire grid.
32111          * @param {Roo.EventObject} e
32112          */
32113         "mouseover" : true,
32114         /**
32115          * @event mouseout
32116          * The raw mouseout event for the entire grid.
32117          * @param {Roo.EventObject} e
32118          */
32119         "mouseout" : true,
32120         /**
32121          * @event keypress
32122          * The raw keypress event for the entire grid.
32123          * @param {Roo.EventObject} e
32124          */
32125         "keypress" : true,
32126         /**
32127          * @event keydown
32128          * The raw keydown event for the entire grid.
32129          * @param {Roo.EventObject} e
32130          */
32131         "keydown" : true,
32132
32133         // custom events
32134
32135         /**
32136          * @event cellclick
32137          * Fires when a cell is clicked
32138          * @param {Grid} this
32139          * @param {Number} rowIndex
32140          * @param {Number} columnIndex
32141          * @param {Roo.EventObject} e
32142          */
32143         "cellclick" : true,
32144         /**
32145          * @event celldblclick
32146          * Fires when a cell is double clicked
32147          * @param {Grid} this
32148          * @param {Number} rowIndex
32149          * @param {Number} columnIndex
32150          * @param {Roo.EventObject} e
32151          */
32152         "celldblclick" : true,
32153         /**
32154          * @event rowclick
32155          * Fires when a row is clicked
32156          * @param {Grid} this
32157          * @param {Number} rowIndex
32158          * @param {Roo.EventObject} e
32159          */
32160         "rowclick" : true,
32161         /**
32162          * @event rowdblclick
32163          * Fires when a row is double clicked
32164          * @param {Grid} this
32165          * @param {Number} rowIndex
32166          * @param {Roo.EventObject} e
32167          */
32168         "rowdblclick" : true,
32169         /**
32170          * @event headerclick
32171          * Fires when a header is clicked
32172          * @param {Grid} this
32173          * @param {Number} columnIndex
32174          * @param {Roo.EventObject} e
32175          */
32176         "headerclick" : true,
32177         /**
32178          * @event headerdblclick
32179          * Fires when a header cell is double clicked
32180          * @param {Grid} this
32181          * @param {Number} columnIndex
32182          * @param {Roo.EventObject} e
32183          */
32184         "headerdblclick" : true,
32185         /**
32186          * @event rowcontextmenu
32187          * Fires when a row is right clicked
32188          * @param {Grid} this
32189          * @param {Number} rowIndex
32190          * @param {Roo.EventObject} e
32191          */
32192         "rowcontextmenu" : true,
32193         /**
32194          * @event cellcontextmenu
32195          * Fires when a cell is right clicked
32196          * @param {Grid} this
32197          * @param {Number} rowIndex
32198          * @param {Number} cellIndex
32199          * @param {Roo.EventObject} e
32200          */
32201          "cellcontextmenu" : true,
32202         /**
32203          * @event headercontextmenu
32204          * Fires when a header is right clicked
32205          * @param {Grid} this
32206          * @param {Number} columnIndex
32207          * @param {Roo.EventObject} e
32208          */
32209         "headercontextmenu" : true,
32210         /**
32211          * @event bodyscroll
32212          * Fires when the body element is scrolled
32213          * @param {Number} scrollLeft
32214          * @param {Number} scrollTop
32215          */
32216         "bodyscroll" : true,
32217         /**
32218          * @event columnresize
32219          * Fires when the user resizes a column
32220          * @param {Number} columnIndex
32221          * @param {Number} newSize
32222          */
32223         "columnresize" : true,
32224         /**
32225          * @event columnmove
32226          * Fires when the user moves a column
32227          * @param {Number} oldIndex
32228          * @param {Number} newIndex
32229          */
32230         "columnmove" : true,
32231         /**
32232          * @event startdrag
32233          * Fires when row(s) start being dragged
32234          * @param {Grid} this
32235          * @param {Roo.GridDD} dd The drag drop object
32236          * @param {event} e The raw browser event
32237          */
32238         "startdrag" : true,
32239         /**
32240          * @event enddrag
32241          * Fires when a drag operation is complete
32242          * @param {Grid} this
32243          * @param {Roo.GridDD} dd The drag drop object
32244          * @param {event} e The raw browser event
32245          */
32246         "enddrag" : true,
32247         /**
32248          * @event dragdrop
32249          * Fires when dragged row(s) are dropped on a valid DD target
32250          * @param {Grid} this
32251          * @param {Roo.GridDD} dd The drag drop object
32252          * @param {String} targetId The target drag drop object
32253          * @param {event} e The raw browser event
32254          */
32255         "dragdrop" : true,
32256         /**
32257          * @event dragover
32258          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32259          * @param {Grid} this
32260          * @param {Roo.GridDD} dd The drag drop object
32261          * @param {String} targetId The target drag drop object
32262          * @param {event} e The raw browser event
32263          */
32264         "dragover" : true,
32265         /**
32266          * @event dragenter
32267          *  Fires when the dragged row(s) first cross another DD target while being dragged
32268          * @param {Grid} this
32269          * @param {Roo.GridDD} dd The drag drop object
32270          * @param {String} targetId The target drag drop object
32271          * @param {event} e The raw browser event
32272          */
32273         "dragenter" : true,
32274         /**
32275          * @event dragout
32276          * Fires when the dragged row(s) leave another DD target while being dragged
32277          * @param {Grid} this
32278          * @param {Roo.GridDD} dd The drag drop object
32279          * @param {String} targetId The target drag drop object
32280          * @param {event} e The raw browser event
32281          */
32282         "dragout" : true,
32283         /**
32284          * @event rowclass
32285          * Fires when a row is rendered, so you can change add a style to it.
32286          * @param {GridView} gridview   The grid view
32287          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32288          */
32289         'rowclass' : true,
32290
32291         /**
32292          * @event render
32293          * Fires when the grid is rendered
32294          * @param {Grid} grid
32295          */
32296         'render' : true
32297     });
32298
32299     Roo.grid.Grid.superclass.constructor.call(this);
32300 };
32301 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32302     
32303     /**
32304      * @cfg {String} ddGroup - drag drop group.
32305      */
32306
32307     /**
32308      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32309      */
32310     minColumnWidth : 25,
32311
32312     /**
32313      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32314      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32315      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32316      */
32317     autoSizeColumns : false,
32318
32319     /**
32320      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32321      */
32322     autoSizeHeaders : true,
32323
32324     /**
32325      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32326      */
32327     monitorWindowResize : true,
32328
32329     /**
32330      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32331      * rows measured to get a columns size. Default is 0 (all rows).
32332      */
32333     maxRowsToMeasure : 0,
32334
32335     /**
32336      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32337      */
32338     trackMouseOver : true,
32339
32340     /**
32341     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32342     */
32343     
32344     /**
32345     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32346     */
32347     enableDragDrop : false,
32348     
32349     /**
32350     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32351     */
32352     enableColumnMove : true,
32353     
32354     /**
32355     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32356     */
32357     enableColumnHide : true,
32358     
32359     /**
32360     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32361     */
32362     enableRowHeightSync : false,
32363     
32364     /**
32365     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32366     */
32367     stripeRows : true,
32368     
32369     /**
32370     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32371     */
32372     autoHeight : false,
32373
32374     /**
32375      * @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.
32376      */
32377     autoExpandColumn : false,
32378
32379     /**
32380     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32381     * Default is 50.
32382     */
32383     autoExpandMin : 50,
32384
32385     /**
32386     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32387     */
32388     autoExpandMax : 1000,
32389
32390     /**
32391     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32392     */
32393     view : null,
32394
32395     /**
32396     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32397     */
32398     loadMask : false,
32399     /**
32400     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32401     */
32402     dropTarget: false,
32403     
32404    
32405     
32406     // private
32407     rendered : false,
32408
32409     /**
32410     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32411     * of a fixed width. Default is false.
32412     */
32413     /**
32414     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32415     */
32416     /**
32417      * Called once after all setup has been completed and the grid is ready to be rendered.
32418      * @return {Roo.grid.Grid} this
32419      */
32420     render : function()
32421     {
32422         var c = this.container;
32423         // try to detect autoHeight/width mode
32424         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32425             this.autoHeight = true;
32426         }
32427         var view = this.getView();
32428         view.init(this);
32429
32430         c.on("click", this.onClick, this);
32431         c.on("dblclick", this.onDblClick, this);
32432         c.on("contextmenu", this.onContextMenu, this);
32433         c.on("keydown", this.onKeyDown, this);
32434
32435         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32436
32437         this.getSelectionModel().init(this);
32438
32439         view.render();
32440
32441         if(this.loadMask){
32442             this.loadMask = new Roo.LoadMask(this.container,
32443                     Roo.apply({store:this.dataSource}, this.loadMask));
32444         }
32445         
32446         
32447         if (this.toolbar && this.toolbar.xtype) {
32448             this.toolbar.container = this.getView().getHeaderPanel(true);
32449             this.toolbar = new Roo.Toolbar(this.toolbar);
32450         }
32451         if (this.footer && this.footer.xtype) {
32452             this.footer.dataSource = this.getDataSource();
32453             this.footer.container = this.getView().getFooterPanel(true);
32454             this.footer = Roo.factory(this.footer, Roo);
32455         }
32456         if (this.dropTarget && this.dropTarget.xtype) {
32457             delete this.dropTarget.xtype;
32458             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32459         }
32460         
32461         
32462         this.rendered = true;
32463         this.fireEvent('render', this);
32464         return this;
32465     },
32466
32467         /**
32468          * Reconfigures the grid to use a different Store and Column Model.
32469          * The View will be bound to the new objects and refreshed.
32470          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32471          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32472          */
32473     reconfigure : function(dataSource, colModel){
32474         if(this.loadMask){
32475             this.loadMask.destroy();
32476             this.loadMask = new Roo.LoadMask(this.container,
32477                     Roo.apply({store:dataSource}, this.loadMask));
32478         }
32479         this.view.bind(dataSource, colModel);
32480         this.dataSource = dataSource;
32481         this.colModel = colModel;
32482         this.view.refresh(true);
32483     },
32484
32485     // private
32486     onKeyDown : function(e){
32487         this.fireEvent("keydown", e);
32488     },
32489
32490     /**
32491      * Destroy this grid.
32492      * @param {Boolean} removeEl True to remove the element
32493      */
32494     destroy : function(removeEl, keepListeners){
32495         if(this.loadMask){
32496             this.loadMask.destroy();
32497         }
32498         var c = this.container;
32499         c.removeAllListeners();
32500         this.view.destroy();
32501         this.colModel.purgeListeners();
32502         if(!keepListeners){
32503             this.purgeListeners();
32504         }
32505         c.update("");
32506         if(removeEl === true){
32507             c.remove();
32508         }
32509     },
32510
32511     // private
32512     processEvent : function(name, e){
32513         this.fireEvent(name, e);
32514         var t = e.getTarget();
32515         var v = this.view;
32516         var header = v.findHeaderIndex(t);
32517         if(header !== false){
32518             this.fireEvent("header" + name, this, header, e);
32519         }else{
32520             var row = v.findRowIndex(t);
32521             var cell = v.findCellIndex(t);
32522             if(row !== false){
32523                 this.fireEvent("row" + name, this, row, e);
32524                 if(cell !== false){
32525                     this.fireEvent("cell" + name, this, row, cell, e);
32526                 }
32527             }
32528         }
32529     },
32530
32531     // private
32532     onClick : function(e){
32533         this.processEvent("click", e);
32534     },
32535
32536     // private
32537     onContextMenu : function(e, t){
32538         this.processEvent("contextmenu", e);
32539     },
32540
32541     // private
32542     onDblClick : function(e){
32543         this.processEvent("dblclick", e);
32544     },
32545
32546     // private
32547     walkCells : function(row, col, step, fn, scope){
32548         var cm = this.colModel, clen = cm.getColumnCount();
32549         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32550         if(step < 0){
32551             if(col < 0){
32552                 row--;
32553                 first = false;
32554             }
32555             while(row >= 0){
32556                 if(!first){
32557                     col = clen-1;
32558                 }
32559                 first = false;
32560                 while(col >= 0){
32561                     if(fn.call(scope || this, row, col, cm) === true){
32562                         return [row, col];
32563                     }
32564                     col--;
32565                 }
32566                 row--;
32567             }
32568         } else {
32569             if(col >= clen){
32570                 row++;
32571                 first = false;
32572             }
32573             while(row < rlen){
32574                 if(!first){
32575                     col = 0;
32576                 }
32577                 first = false;
32578                 while(col < clen){
32579                     if(fn.call(scope || this, row, col, cm) === true){
32580                         return [row, col];
32581                     }
32582                     col++;
32583                 }
32584                 row++;
32585             }
32586         }
32587         return null;
32588     },
32589
32590     // private
32591     getSelections : function(){
32592         return this.selModel.getSelections();
32593     },
32594
32595     /**
32596      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32597      * but if manual update is required this method will initiate it.
32598      */
32599     autoSize : function(){
32600         if(this.rendered){
32601             this.view.layout();
32602             if(this.view.adjustForScroll){
32603                 this.view.adjustForScroll();
32604             }
32605         }
32606     },
32607
32608     /**
32609      * Returns the grid's underlying element.
32610      * @return {Element} The element
32611      */
32612     getGridEl : function(){
32613         return this.container;
32614     },
32615
32616     // private for compatibility, overridden by editor grid
32617     stopEditing : function(){},
32618
32619     /**
32620      * Returns the grid's SelectionModel.
32621      * @return {SelectionModel}
32622      */
32623     getSelectionModel : function(){
32624         if(!this.selModel){
32625             this.selModel = new Roo.grid.RowSelectionModel();
32626         }
32627         return this.selModel;
32628     },
32629
32630     /**
32631      * Returns the grid's DataSource.
32632      * @return {DataSource}
32633      */
32634     getDataSource : function(){
32635         return this.dataSource;
32636     },
32637
32638     /**
32639      * Returns the grid's ColumnModel.
32640      * @return {ColumnModel}
32641      */
32642     getColumnModel : function(){
32643         return this.colModel;
32644     },
32645
32646     /**
32647      * Returns the grid's GridView object.
32648      * @return {GridView}
32649      */
32650     getView : function(){
32651         if(!this.view){
32652             this.view = new Roo.grid.GridView(this.viewConfig);
32653         }
32654         return this.view;
32655     },
32656     /**
32657      * Called to get grid's drag proxy text, by default returns this.ddText.
32658      * @return {String}
32659      */
32660     getDragDropText : function(){
32661         var count = this.selModel.getCount();
32662         return String.format(this.ddText, count, count == 1 ? '' : 's');
32663     }
32664 });
32665 /**
32666  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32667  * %0 is replaced with the number of selected rows.
32668  * @type String
32669  */
32670 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32671  * Based on:
32672  * Ext JS Library 1.1.1
32673  * Copyright(c) 2006-2007, Ext JS, LLC.
32674  *
32675  * Originally Released Under LGPL - original licence link has changed is not relivant.
32676  *
32677  * Fork - LGPL
32678  * <script type="text/javascript">
32679  */
32680  
32681 Roo.grid.AbstractGridView = function(){
32682         this.grid = null;
32683         
32684         this.events = {
32685             "beforerowremoved" : true,
32686             "beforerowsinserted" : true,
32687             "beforerefresh" : true,
32688             "rowremoved" : true,
32689             "rowsinserted" : true,
32690             "rowupdated" : true,
32691             "refresh" : true
32692         };
32693     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32694 };
32695
32696 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32697     rowClass : "x-grid-row",
32698     cellClass : "x-grid-cell",
32699     tdClass : "x-grid-td",
32700     hdClass : "x-grid-hd",
32701     splitClass : "x-grid-hd-split",
32702     
32703         init: function(grid){
32704         this.grid = grid;
32705                 var cid = this.grid.getGridEl().id;
32706         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32707         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32708         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32709         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32710         },
32711         
32712         getColumnRenderers : function(){
32713         var renderers = [];
32714         var cm = this.grid.colModel;
32715         var colCount = cm.getColumnCount();
32716         for(var i = 0; i < colCount; i++){
32717             renderers[i] = cm.getRenderer(i);
32718         }
32719         return renderers;
32720     },
32721     
32722     getColumnIds : function(){
32723         var ids = [];
32724         var cm = this.grid.colModel;
32725         var colCount = cm.getColumnCount();
32726         for(var i = 0; i < colCount; i++){
32727             ids[i] = cm.getColumnId(i);
32728         }
32729         return ids;
32730     },
32731     
32732     getDataIndexes : function(){
32733         if(!this.indexMap){
32734             this.indexMap = this.buildIndexMap();
32735         }
32736         return this.indexMap.colToData;
32737     },
32738     
32739     getColumnIndexByDataIndex : function(dataIndex){
32740         if(!this.indexMap){
32741             this.indexMap = this.buildIndexMap();
32742         }
32743         return this.indexMap.dataToCol[dataIndex];
32744     },
32745     
32746     /**
32747      * Set a css style for a column dynamically. 
32748      * @param {Number} colIndex The index of the column
32749      * @param {String} name The css property name
32750      * @param {String} value The css value
32751      */
32752     setCSSStyle : function(colIndex, name, value){
32753         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32754         Roo.util.CSS.updateRule(selector, name, value);
32755     },
32756     
32757     generateRules : function(cm){
32758         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32759         Roo.util.CSS.removeStyleSheet(rulesId);
32760         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32761             var cid = cm.getColumnId(i);
32762             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32763                          this.tdSelector, cid, " {\n}\n",
32764                          this.hdSelector, cid, " {\n}\n",
32765                          this.splitSelector, cid, " {\n}\n");
32766         }
32767         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32768     }
32769 });/*
32770  * Based on:
32771  * Ext JS Library 1.1.1
32772  * Copyright(c) 2006-2007, Ext JS, LLC.
32773  *
32774  * Originally Released Under LGPL - original licence link has changed is not relivant.
32775  *
32776  * Fork - LGPL
32777  * <script type="text/javascript">
32778  */
32779
32780 // private
32781 // This is a support class used internally by the Grid components
32782 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32783     this.grid = grid;
32784     this.view = grid.getView();
32785     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32786     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32787     if(hd2){
32788         this.setHandleElId(Roo.id(hd));
32789         this.setOuterHandleElId(Roo.id(hd2));
32790     }
32791     this.scroll = false;
32792 };
32793 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32794     maxDragWidth: 120,
32795     getDragData : function(e){
32796         var t = Roo.lib.Event.getTarget(e);
32797         var h = this.view.findHeaderCell(t);
32798         if(h){
32799             return {ddel: h.firstChild, header:h};
32800         }
32801         return false;
32802     },
32803
32804     onInitDrag : function(e){
32805         this.view.headersDisabled = true;
32806         var clone = this.dragData.ddel.cloneNode(true);
32807         clone.id = Roo.id();
32808         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32809         this.proxy.update(clone);
32810         return true;
32811     },
32812
32813     afterValidDrop : function(){
32814         var v = this.view;
32815         setTimeout(function(){
32816             v.headersDisabled = false;
32817         }, 50);
32818     },
32819
32820     afterInvalidDrop : function(){
32821         var v = this.view;
32822         setTimeout(function(){
32823             v.headersDisabled = false;
32824         }, 50);
32825     }
32826 });
32827 /*
32828  * Based on:
32829  * Ext JS Library 1.1.1
32830  * Copyright(c) 2006-2007, Ext JS, LLC.
32831  *
32832  * Originally Released Under LGPL - original licence link has changed is not relivant.
32833  *
32834  * Fork - LGPL
32835  * <script type="text/javascript">
32836  */
32837 // private
32838 // This is a support class used internally by the Grid components
32839 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32840     this.grid = grid;
32841     this.view = grid.getView();
32842     // split the proxies so they don't interfere with mouse events
32843     this.proxyTop = Roo.DomHelper.append(document.body, {
32844         cls:"col-move-top", html:"&#160;"
32845     }, true);
32846     this.proxyBottom = Roo.DomHelper.append(document.body, {
32847         cls:"col-move-bottom", html:"&#160;"
32848     }, true);
32849     this.proxyTop.hide = this.proxyBottom.hide = function(){
32850         this.setLeftTop(-100,-100);
32851         this.setStyle("visibility", "hidden");
32852     };
32853     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32854     // temporarily disabled
32855     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32856     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32857 };
32858 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32859     proxyOffsets : [-4, -9],
32860     fly: Roo.Element.fly,
32861
32862     getTargetFromEvent : function(e){
32863         var t = Roo.lib.Event.getTarget(e);
32864         var cindex = this.view.findCellIndex(t);
32865         if(cindex !== false){
32866             return this.view.getHeaderCell(cindex);
32867         }
32868         return null;
32869     },
32870
32871     nextVisible : function(h){
32872         var v = this.view, cm = this.grid.colModel;
32873         h = h.nextSibling;
32874         while(h){
32875             if(!cm.isHidden(v.getCellIndex(h))){
32876                 return h;
32877             }
32878             h = h.nextSibling;
32879         }
32880         return null;
32881     },
32882
32883     prevVisible : function(h){
32884         var v = this.view, cm = this.grid.colModel;
32885         h = h.prevSibling;
32886         while(h){
32887             if(!cm.isHidden(v.getCellIndex(h))){
32888                 return h;
32889             }
32890             h = h.prevSibling;
32891         }
32892         return null;
32893     },
32894
32895     positionIndicator : function(h, n, e){
32896         var x = Roo.lib.Event.getPageX(e);
32897         var r = Roo.lib.Dom.getRegion(n.firstChild);
32898         var px, pt, py = r.top + this.proxyOffsets[1];
32899         if((r.right - x) <= (r.right-r.left)/2){
32900             px = r.right+this.view.borderWidth;
32901             pt = "after";
32902         }else{
32903             px = r.left;
32904             pt = "before";
32905         }
32906         var oldIndex = this.view.getCellIndex(h);
32907         var newIndex = this.view.getCellIndex(n);
32908
32909         if(this.grid.colModel.isFixed(newIndex)){
32910             return false;
32911         }
32912
32913         var locked = this.grid.colModel.isLocked(newIndex);
32914
32915         if(pt == "after"){
32916             newIndex++;
32917         }
32918         if(oldIndex < newIndex){
32919             newIndex--;
32920         }
32921         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32922             return false;
32923         }
32924         px +=  this.proxyOffsets[0];
32925         this.proxyTop.setLeftTop(px, py);
32926         this.proxyTop.show();
32927         if(!this.bottomOffset){
32928             this.bottomOffset = this.view.mainHd.getHeight();
32929         }
32930         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32931         this.proxyBottom.show();
32932         return pt;
32933     },
32934
32935     onNodeEnter : function(n, dd, e, data){
32936         if(data.header != n){
32937             this.positionIndicator(data.header, n, e);
32938         }
32939     },
32940
32941     onNodeOver : function(n, dd, e, data){
32942         var result = false;
32943         if(data.header != n){
32944             result = this.positionIndicator(data.header, n, e);
32945         }
32946         if(!result){
32947             this.proxyTop.hide();
32948             this.proxyBottom.hide();
32949         }
32950         return result ? this.dropAllowed : this.dropNotAllowed;
32951     },
32952
32953     onNodeOut : function(n, dd, e, data){
32954         this.proxyTop.hide();
32955         this.proxyBottom.hide();
32956     },
32957
32958     onNodeDrop : function(n, dd, e, data){
32959         var h = data.header;
32960         if(h != n){
32961             var cm = this.grid.colModel;
32962             var x = Roo.lib.Event.getPageX(e);
32963             var r = Roo.lib.Dom.getRegion(n.firstChild);
32964             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32965             var oldIndex = this.view.getCellIndex(h);
32966             var newIndex = this.view.getCellIndex(n);
32967             var locked = cm.isLocked(newIndex);
32968             if(pt == "after"){
32969                 newIndex++;
32970             }
32971             if(oldIndex < newIndex){
32972                 newIndex--;
32973             }
32974             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32975                 return false;
32976             }
32977             cm.setLocked(oldIndex, locked, true);
32978             cm.moveColumn(oldIndex, newIndex);
32979             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32980             return true;
32981         }
32982         return false;
32983     }
32984 });
32985 /*
32986  * Based on:
32987  * Ext JS Library 1.1.1
32988  * Copyright(c) 2006-2007, Ext JS, LLC.
32989  *
32990  * Originally Released Under LGPL - original licence link has changed is not relivant.
32991  *
32992  * Fork - LGPL
32993  * <script type="text/javascript">
32994  */
32995   
32996 /**
32997  * @class Roo.grid.GridView
32998  * @extends Roo.util.Observable
32999  *
33000  * @constructor
33001  * @param {Object} config
33002  */
33003 Roo.grid.GridView = function(config){
33004     Roo.grid.GridView.superclass.constructor.call(this);
33005     this.el = null;
33006
33007     Roo.apply(this, config);
33008 };
33009
33010 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33011
33012     /**
33013      * Override this function to apply custom css classes to rows during rendering
33014      * @param {Record} record The record
33015      * @param {Number} index
33016      * @method getRowClass
33017      */
33018     rowClass : "x-grid-row",
33019
33020     cellClass : "x-grid-col",
33021
33022     tdClass : "x-grid-td",
33023
33024     hdClass : "x-grid-hd",
33025
33026     splitClass : "x-grid-split",
33027
33028     sortClasses : ["sort-asc", "sort-desc"],
33029
33030     enableMoveAnim : false,
33031
33032     hlColor: "C3DAF9",
33033
33034     dh : Roo.DomHelper,
33035
33036     fly : Roo.Element.fly,
33037
33038     css : Roo.util.CSS,
33039
33040     borderWidth: 1,
33041
33042     splitOffset: 3,
33043
33044     scrollIncrement : 22,
33045
33046     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33047
33048     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33049
33050     bind : function(ds, cm){
33051         if(this.ds){
33052             this.ds.un("load", this.onLoad, this);
33053             this.ds.un("datachanged", this.onDataChange, this);
33054             this.ds.un("add", this.onAdd, this);
33055             this.ds.un("remove", this.onRemove, this);
33056             this.ds.un("update", this.onUpdate, this);
33057             this.ds.un("clear", this.onClear, this);
33058         }
33059         if(ds){
33060             ds.on("load", this.onLoad, this);
33061             ds.on("datachanged", this.onDataChange, this);
33062             ds.on("add", this.onAdd, this);
33063             ds.on("remove", this.onRemove, this);
33064             ds.on("update", this.onUpdate, this);
33065             ds.on("clear", this.onClear, this);
33066         }
33067         this.ds = ds;
33068
33069         if(this.cm){
33070             this.cm.un("widthchange", this.onColWidthChange, this);
33071             this.cm.un("headerchange", this.onHeaderChange, this);
33072             this.cm.un("hiddenchange", this.onHiddenChange, this);
33073             this.cm.un("columnmoved", this.onColumnMove, this);
33074             this.cm.un("columnlockchange", this.onColumnLock, this);
33075         }
33076         if(cm){
33077             this.generateRules(cm);
33078             cm.on("widthchange", this.onColWidthChange, this);
33079             cm.on("headerchange", this.onHeaderChange, this);
33080             cm.on("hiddenchange", this.onHiddenChange, this);
33081             cm.on("columnmoved", this.onColumnMove, this);
33082             cm.on("columnlockchange", this.onColumnLock, this);
33083         }
33084         this.cm = cm;
33085     },
33086
33087     init: function(grid){
33088         Roo.grid.GridView.superclass.init.call(this, grid);
33089
33090         this.bind(grid.dataSource, grid.colModel);
33091
33092         grid.on("headerclick", this.handleHeaderClick, this);
33093
33094         if(grid.trackMouseOver){
33095             grid.on("mouseover", this.onRowOver, this);
33096             grid.on("mouseout", this.onRowOut, this);
33097         }
33098         grid.cancelTextSelection = function(){};
33099         this.gridId = grid.id;
33100
33101         var tpls = this.templates || {};
33102
33103         if(!tpls.master){
33104             tpls.master = new Roo.Template(
33105                '<div class="x-grid" hidefocus="true">',
33106                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33107                   '<div class="x-grid-topbar"></div>',
33108                   '<div class="x-grid-scroller"><div></div></div>',
33109                   '<div class="x-grid-locked">',
33110                       '<div class="x-grid-header">{lockedHeader}</div>',
33111                       '<div class="x-grid-body">{lockedBody}</div>',
33112                   "</div>",
33113                   '<div class="x-grid-viewport">',
33114                       '<div class="x-grid-header">{header}</div>',
33115                       '<div class="x-grid-body">{body}</div>',
33116                   "</div>",
33117                   '<div class="x-grid-bottombar"></div>',
33118                  
33119                   '<div class="x-grid-resize-proxy">&#160;</div>',
33120                "</div>"
33121             );
33122             tpls.master.disableformats = true;
33123         }
33124
33125         if(!tpls.header){
33126             tpls.header = new Roo.Template(
33127                '<table border="0" cellspacing="0" cellpadding="0">',
33128                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33129                "</table>{splits}"
33130             );
33131             tpls.header.disableformats = true;
33132         }
33133         tpls.header.compile();
33134
33135         if(!tpls.hcell){
33136             tpls.hcell = new Roo.Template(
33137                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33138                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33139                 "</div></td>"
33140              );
33141              tpls.hcell.disableFormats = true;
33142         }
33143         tpls.hcell.compile();
33144
33145         if(!tpls.hsplit){
33146             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
33147             tpls.hsplit.disableFormats = true;
33148         }
33149         tpls.hsplit.compile();
33150
33151         if(!tpls.body){
33152             tpls.body = new Roo.Template(
33153                '<table border="0" cellspacing="0" cellpadding="0">',
33154                "<tbody>{rows}</tbody>",
33155                "</table>"
33156             );
33157             tpls.body.disableFormats = true;
33158         }
33159         tpls.body.compile();
33160
33161         if(!tpls.row){
33162             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33163             tpls.row.disableFormats = true;
33164         }
33165         tpls.row.compile();
33166
33167         if(!tpls.cell){
33168             tpls.cell = new Roo.Template(
33169                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33170                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
33171                 "</td>"
33172             );
33173             tpls.cell.disableFormats = true;
33174         }
33175         tpls.cell.compile();
33176
33177         this.templates = tpls;
33178     },
33179
33180     // remap these for backwards compat
33181     onColWidthChange : function(){
33182         this.updateColumns.apply(this, arguments);
33183     },
33184     onHeaderChange : function(){
33185         this.updateHeaders.apply(this, arguments);
33186     }, 
33187     onHiddenChange : function(){
33188         this.handleHiddenChange.apply(this, arguments);
33189     },
33190     onColumnMove : function(){
33191         this.handleColumnMove.apply(this, arguments);
33192     },
33193     onColumnLock : function(){
33194         this.handleLockChange.apply(this, arguments);
33195     },
33196
33197     onDataChange : function(){
33198         this.refresh();
33199         this.updateHeaderSortState();
33200     },
33201
33202     onClear : function(){
33203         this.refresh();
33204     },
33205
33206     onUpdate : function(ds, record){
33207         this.refreshRow(record);
33208     },
33209
33210     refreshRow : function(record){
33211         var ds = this.ds, index;
33212         if(typeof record == 'number'){
33213             index = record;
33214             record = ds.getAt(index);
33215         }else{
33216             index = ds.indexOf(record);
33217         }
33218         this.insertRows(ds, index, index, true);
33219         this.onRemove(ds, record, index+1, true);
33220         this.syncRowHeights(index, index);
33221         this.layout();
33222         this.fireEvent("rowupdated", this, index, record);
33223     },
33224
33225     onAdd : function(ds, records, index){
33226         this.insertRows(ds, index, index + (records.length-1));
33227     },
33228
33229     onRemove : function(ds, record, index, isUpdate){
33230         if(isUpdate !== true){
33231             this.fireEvent("beforerowremoved", this, index, record);
33232         }
33233         var bt = this.getBodyTable(), lt = this.getLockedTable();
33234         if(bt.rows[index]){
33235             bt.firstChild.removeChild(bt.rows[index]);
33236         }
33237         if(lt.rows[index]){
33238             lt.firstChild.removeChild(lt.rows[index]);
33239         }
33240         if(isUpdate !== true){
33241             this.stripeRows(index);
33242             this.syncRowHeights(index, index);
33243             this.layout();
33244             this.fireEvent("rowremoved", this, index, record);
33245         }
33246     },
33247
33248     onLoad : function(){
33249         this.scrollToTop();
33250     },
33251
33252     /**
33253      * Scrolls the grid to the top
33254      */
33255     scrollToTop : function(){
33256         if(this.scroller){
33257             this.scroller.dom.scrollTop = 0;
33258             this.syncScroll();
33259         }
33260     },
33261
33262     /**
33263      * Gets a panel in the header of the grid that can be used for toolbars etc.
33264      * After modifying the contents of this panel a call to grid.autoSize() may be
33265      * required to register any changes in size.
33266      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33267      * @return Roo.Element
33268      */
33269     getHeaderPanel : function(doShow){
33270         if(doShow){
33271             this.headerPanel.show();
33272         }
33273         return this.headerPanel;
33274     },
33275
33276     /**
33277      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33278      * After modifying the contents of this panel a call to grid.autoSize() may be
33279      * required to register any changes in size.
33280      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33281      * @return Roo.Element
33282      */
33283     getFooterPanel : function(doShow){
33284         if(doShow){
33285             this.footerPanel.show();
33286         }
33287         return this.footerPanel;
33288     },
33289
33290     initElements : function(){
33291         var E = Roo.Element;
33292         var el = this.grid.getGridEl().dom.firstChild;
33293         var cs = el.childNodes;
33294
33295         this.el = new E(el);
33296         
33297          this.focusEl = new E(el.firstChild);
33298         this.focusEl.swallowEvent("click", true);
33299         
33300         this.headerPanel = new E(cs[1]);
33301         this.headerPanel.enableDisplayMode("block");
33302
33303         this.scroller = new E(cs[2]);
33304         this.scrollSizer = new E(this.scroller.dom.firstChild);
33305
33306         this.lockedWrap = new E(cs[3]);
33307         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33308         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33309
33310         this.mainWrap = new E(cs[4]);
33311         this.mainHd = new E(this.mainWrap.dom.firstChild);
33312         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33313
33314         this.footerPanel = new E(cs[5]);
33315         this.footerPanel.enableDisplayMode("block");
33316
33317         this.resizeProxy = new E(cs[6]);
33318
33319         this.headerSelector = String.format(
33320            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33321            this.lockedHd.id, this.mainHd.id
33322         );
33323
33324         this.splitterSelector = String.format(
33325            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33326            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33327         );
33328     },
33329     idToCssName : function(s)
33330     {
33331         return s.replace(/[^a-z0-9]+/ig, '-');
33332     },
33333
33334     getHeaderCell : function(index){
33335         return Roo.DomQuery.select(this.headerSelector)[index];
33336     },
33337
33338     getHeaderCellMeasure : function(index){
33339         return this.getHeaderCell(index).firstChild;
33340     },
33341
33342     getHeaderCellText : function(index){
33343         return this.getHeaderCell(index).firstChild.firstChild;
33344     },
33345
33346     getLockedTable : function(){
33347         return this.lockedBody.dom.firstChild;
33348     },
33349
33350     getBodyTable : function(){
33351         return this.mainBody.dom.firstChild;
33352     },
33353
33354     getLockedRow : function(index){
33355         return this.getLockedTable().rows[index];
33356     },
33357
33358     getRow : function(index){
33359         return this.getBodyTable().rows[index];
33360     },
33361
33362     getRowComposite : function(index){
33363         if(!this.rowEl){
33364             this.rowEl = new Roo.CompositeElementLite();
33365         }
33366         var els = [], lrow, mrow;
33367         if(lrow = this.getLockedRow(index)){
33368             els.push(lrow);
33369         }
33370         if(mrow = this.getRow(index)){
33371             els.push(mrow);
33372         }
33373         this.rowEl.elements = els;
33374         return this.rowEl;
33375     },
33376
33377     getCell : function(rowIndex, colIndex){
33378         var locked = this.cm.getLockedCount();
33379         var source;
33380         if(colIndex < locked){
33381             source = this.lockedBody.dom.firstChild;
33382         }else{
33383             source = this.mainBody.dom.firstChild;
33384             colIndex -= locked;
33385         }
33386         return source.rows[rowIndex].childNodes[colIndex];
33387     },
33388
33389     getCellText : function(rowIndex, colIndex){
33390         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33391     },
33392
33393     getCellBox : function(cell){
33394         var b = this.fly(cell).getBox();
33395         if(Roo.isOpera){ // opera fails to report the Y
33396             b.y = cell.offsetTop + this.mainBody.getY();
33397         }
33398         return b;
33399     },
33400
33401     getCellIndex : function(cell){
33402         var id = String(cell.className).match(this.cellRE);
33403         if(id){
33404             return parseInt(id[1], 10);
33405         }
33406         return 0;
33407     },
33408
33409     findHeaderIndex : function(n){
33410         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33411         return r ? this.getCellIndex(r) : false;
33412     },
33413
33414     findHeaderCell : function(n){
33415         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33416         return r ? r : false;
33417     },
33418
33419     findRowIndex : function(n){
33420         if(!n){
33421             return false;
33422         }
33423         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33424         return r ? r.rowIndex : false;
33425     },
33426
33427     findCellIndex : function(node){
33428         var stop = this.el.dom;
33429         while(node && node != stop){
33430             if(this.findRE.test(node.className)){
33431                 return this.getCellIndex(node);
33432             }
33433             node = node.parentNode;
33434         }
33435         return false;
33436     },
33437
33438     getColumnId : function(index){
33439         return this.cm.getColumnId(index);
33440     },
33441
33442     getSplitters : function()
33443     {
33444         if(this.splitterSelector){
33445            return Roo.DomQuery.select(this.splitterSelector);
33446         }else{
33447             return null;
33448       }
33449     },
33450
33451     getSplitter : function(index){
33452         return this.getSplitters()[index];
33453     },
33454
33455     onRowOver : function(e, t){
33456         var row;
33457         if((row = this.findRowIndex(t)) !== false){
33458             this.getRowComposite(row).addClass("x-grid-row-over");
33459         }
33460     },
33461
33462     onRowOut : function(e, t){
33463         var row;
33464         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33465             this.getRowComposite(row).removeClass("x-grid-row-over");
33466         }
33467     },
33468
33469     renderHeaders : function(){
33470         var cm = this.cm;
33471         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33472         var cb = [], lb = [], sb = [], lsb = [], p = {};
33473         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33474             p.cellId = "x-grid-hd-0-" + i;
33475             p.splitId = "x-grid-csplit-0-" + i;
33476             p.id = cm.getColumnId(i);
33477             p.title = cm.getColumnTooltip(i) || "";
33478             p.value = cm.getColumnHeader(i) || "";
33479             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33480             if(!cm.isLocked(i)){
33481                 cb[cb.length] = ct.apply(p);
33482                 sb[sb.length] = st.apply(p);
33483             }else{
33484                 lb[lb.length] = ct.apply(p);
33485                 lsb[lsb.length] = st.apply(p);
33486             }
33487         }
33488         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33489                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33490     },
33491
33492     updateHeaders : function(){
33493         var html = this.renderHeaders();
33494         this.lockedHd.update(html[0]);
33495         this.mainHd.update(html[1]);
33496     },
33497
33498     /**
33499      * Focuses the specified row.
33500      * @param {Number} row The row index
33501      */
33502     focusRow : function(row)
33503     {
33504         //Roo.log('GridView.focusRow');
33505         var x = this.scroller.dom.scrollLeft;
33506         this.focusCell(row, 0, false);
33507         this.scroller.dom.scrollLeft = x;
33508     },
33509
33510     /**
33511      * Focuses the specified cell.
33512      * @param {Number} row The row index
33513      * @param {Number} col The column index
33514      * @param {Boolean} hscroll false to disable horizontal scrolling
33515      */
33516     focusCell : function(row, col, hscroll)
33517     {
33518         //Roo.log('GridView.focusCell');
33519         var el = this.ensureVisible(row, col, hscroll);
33520         this.focusEl.alignTo(el, "tl-tl");
33521         if(Roo.isGecko){
33522             this.focusEl.focus();
33523         }else{
33524             this.focusEl.focus.defer(1, this.focusEl);
33525         }
33526     },
33527
33528     /**
33529      * Scrolls the specified cell into view
33530      * @param {Number} row The row index
33531      * @param {Number} col The column index
33532      * @param {Boolean} hscroll false to disable horizontal scrolling
33533      */
33534     ensureVisible : function(row, col, hscroll)
33535     {
33536         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33537         //return null; //disable for testing.
33538         if(typeof row != "number"){
33539             row = row.rowIndex;
33540         }
33541         if(row < 0 && row >= this.ds.getCount()){
33542             return  null;
33543         }
33544         col = (col !== undefined ? col : 0);
33545         var cm = this.grid.colModel;
33546         while(cm.isHidden(col)){
33547             col++;
33548         }
33549
33550         var el = this.getCell(row, col);
33551         if(!el){
33552             return null;
33553         }
33554         var c = this.scroller.dom;
33555
33556         var ctop = parseInt(el.offsetTop, 10);
33557         var cleft = parseInt(el.offsetLeft, 10);
33558         var cbot = ctop + el.offsetHeight;
33559         var cright = cleft + el.offsetWidth;
33560         
33561         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33562         var stop = parseInt(c.scrollTop, 10);
33563         var sleft = parseInt(c.scrollLeft, 10);
33564         var sbot = stop + ch;
33565         var sright = sleft + c.clientWidth;
33566         /*
33567         Roo.log('GridView.ensureVisible:' +
33568                 ' ctop:' + ctop +
33569                 ' c.clientHeight:' + c.clientHeight +
33570                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33571                 ' stop:' + stop +
33572                 ' cbot:' + cbot +
33573                 ' sbot:' + sbot +
33574                 ' ch:' + ch  
33575                 );
33576         */
33577         if(ctop < stop){
33578              c.scrollTop = ctop;
33579             //Roo.log("set scrolltop to ctop DISABLE?");
33580         }else if(cbot > sbot){
33581             //Roo.log("set scrolltop to cbot-ch");
33582             c.scrollTop = cbot-ch;
33583         }
33584         
33585         if(hscroll !== false){
33586             if(cleft < sleft){
33587                 c.scrollLeft = cleft;
33588             }else if(cright > sright){
33589                 c.scrollLeft = cright-c.clientWidth;
33590             }
33591         }
33592          
33593         return el;
33594     },
33595
33596     updateColumns : function(){
33597         this.grid.stopEditing();
33598         var cm = this.grid.colModel, colIds = this.getColumnIds();
33599         //var totalWidth = cm.getTotalWidth();
33600         var pos = 0;
33601         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33602             //if(cm.isHidden(i)) continue;
33603             var w = cm.getColumnWidth(i);
33604             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33605             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33606         }
33607         this.updateSplitters();
33608     },
33609
33610     generateRules : function(cm){
33611         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33612         Roo.util.CSS.removeStyleSheet(rulesId);
33613         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33614             var cid = cm.getColumnId(i);
33615             var align = '';
33616             if(cm.config[i].align){
33617                 align = 'text-align:'+cm.config[i].align+';';
33618             }
33619             var hidden = '';
33620             if(cm.isHidden(i)){
33621                 hidden = 'display:none;';
33622             }
33623             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33624             ruleBuf.push(
33625                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33626                     this.hdSelector, cid, " {\n", align, width, "}\n",
33627                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33628                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33629         }
33630         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33631     },
33632
33633     updateSplitters : function(){
33634         var cm = this.cm, s = this.getSplitters();
33635         if(s){ // splitters not created yet
33636             var pos = 0, locked = true;
33637             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33638                 if(cm.isHidden(i)) continue;
33639                 var w = cm.getColumnWidth(i); // make sure it's a number
33640                 if(!cm.isLocked(i) && locked){
33641                     pos = 0;
33642                     locked = false;
33643                 }
33644                 pos += w;
33645                 s[i].style.left = (pos-this.splitOffset) + "px";
33646             }
33647         }
33648     },
33649
33650     handleHiddenChange : function(colModel, colIndex, hidden){
33651         if(hidden){
33652             this.hideColumn(colIndex);
33653         }else{
33654             this.unhideColumn(colIndex);
33655         }
33656     },
33657
33658     hideColumn : function(colIndex){
33659         var cid = this.getColumnId(colIndex);
33660         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33661         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33662         if(Roo.isSafari){
33663             this.updateHeaders();
33664         }
33665         this.updateSplitters();
33666         this.layout();
33667     },
33668
33669     unhideColumn : function(colIndex){
33670         var cid = this.getColumnId(colIndex);
33671         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33672         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33673
33674         if(Roo.isSafari){
33675             this.updateHeaders();
33676         }
33677         this.updateSplitters();
33678         this.layout();
33679     },
33680
33681     insertRows : function(dm, firstRow, lastRow, isUpdate){
33682         if(firstRow == 0 && lastRow == dm.getCount()-1){
33683             this.refresh();
33684         }else{
33685             if(!isUpdate){
33686                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33687             }
33688             var s = this.getScrollState();
33689             var markup = this.renderRows(firstRow, lastRow);
33690             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33691             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33692             this.restoreScroll(s);
33693             if(!isUpdate){
33694                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33695                 this.syncRowHeights(firstRow, lastRow);
33696                 this.stripeRows(firstRow);
33697                 this.layout();
33698             }
33699         }
33700     },
33701
33702     bufferRows : function(markup, target, index){
33703         var before = null, trows = target.rows, tbody = target.tBodies[0];
33704         if(index < trows.length){
33705             before = trows[index];
33706         }
33707         var b = document.createElement("div");
33708         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33709         var rows = b.firstChild.rows;
33710         for(var i = 0, len = rows.length; i < len; i++){
33711             if(before){
33712                 tbody.insertBefore(rows[0], before);
33713             }else{
33714                 tbody.appendChild(rows[0]);
33715             }
33716         }
33717         b.innerHTML = "";
33718         b = null;
33719     },
33720
33721     deleteRows : function(dm, firstRow, lastRow){
33722         if(dm.getRowCount()<1){
33723             this.fireEvent("beforerefresh", this);
33724             this.mainBody.update("");
33725             this.lockedBody.update("");
33726             this.fireEvent("refresh", this);
33727         }else{
33728             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33729             var bt = this.getBodyTable();
33730             var tbody = bt.firstChild;
33731             var rows = bt.rows;
33732             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33733                 tbody.removeChild(rows[firstRow]);
33734             }
33735             this.stripeRows(firstRow);
33736             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33737         }
33738     },
33739
33740     updateRows : function(dataSource, firstRow, lastRow){
33741         var s = this.getScrollState();
33742         this.refresh();
33743         this.restoreScroll(s);
33744     },
33745
33746     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33747         if(!noRefresh){
33748            this.refresh();
33749         }
33750         this.updateHeaderSortState();
33751     },
33752
33753     getScrollState : function(){
33754         
33755         var sb = this.scroller.dom;
33756         return {left: sb.scrollLeft, top: sb.scrollTop};
33757     },
33758
33759     stripeRows : function(startRow){
33760         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33761             return;
33762         }
33763         startRow = startRow || 0;
33764         var rows = this.getBodyTable().rows;
33765         var lrows = this.getLockedTable().rows;
33766         var cls = ' x-grid-row-alt ';
33767         for(var i = startRow, len = rows.length; i < len; i++){
33768             var row = rows[i], lrow = lrows[i];
33769             var isAlt = ((i+1) % 2 == 0);
33770             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33771             if(isAlt == hasAlt){
33772                 continue;
33773             }
33774             if(isAlt){
33775                 row.className += " x-grid-row-alt";
33776             }else{
33777                 row.className = row.className.replace("x-grid-row-alt", "");
33778             }
33779             if(lrow){
33780                 lrow.className = row.className;
33781             }
33782         }
33783     },
33784
33785     restoreScroll : function(state){
33786         //Roo.log('GridView.restoreScroll');
33787         var sb = this.scroller.dom;
33788         sb.scrollLeft = state.left;
33789         sb.scrollTop = state.top;
33790         this.syncScroll();
33791     },
33792
33793     syncScroll : function(){
33794         //Roo.log('GridView.syncScroll');
33795         var sb = this.scroller.dom;
33796         var sh = this.mainHd.dom;
33797         var bs = this.mainBody.dom;
33798         var lv = this.lockedBody.dom;
33799         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33800         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33801     },
33802
33803     handleScroll : function(e){
33804         this.syncScroll();
33805         var sb = this.scroller.dom;
33806         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33807         e.stopEvent();
33808     },
33809
33810     handleWheel : function(e){
33811         var d = e.getWheelDelta();
33812         this.scroller.dom.scrollTop -= d*22;
33813         // set this here to prevent jumpy scrolling on large tables
33814         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33815         e.stopEvent();
33816     },
33817
33818     renderRows : function(startRow, endRow){
33819         // pull in all the crap needed to render rows
33820         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33821         var colCount = cm.getColumnCount();
33822
33823         if(ds.getCount() < 1){
33824             return ["", ""];
33825         }
33826
33827         // build a map for all the columns
33828         var cs = [];
33829         for(var i = 0; i < colCount; i++){
33830             var name = cm.getDataIndex(i);
33831             cs[i] = {
33832                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33833                 renderer : cm.getRenderer(i),
33834                 id : cm.getColumnId(i),
33835                 locked : cm.isLocked(i)
33836             };
33837         }
33838
33839         startRow = startRow || 0;
33840         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33841
33842         // records to render
33843         var rs = ds.getRange(startRow, endRow);
33844
33845         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33846     },
33847
33848     // As much as I hate to duplicate code, this was branched because FireFox really hates
33849     // [].join("") on strings. The performance difference was substantial enough to
33850     // branch this function
33851     doRender : Roo.isGecko ?
33852             function(cs, rs, ds, startRow, colCount, stripe){
33853                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33854                 // buffers
33855                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33856                 
33857                 var hasListener = this.grid.hasListener('rowclass');
33858                 var rowcfg = {};
33859                 for(var j = 0, len = rs.length; j < len; j++){
33860                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33861                     for(var i = 0; i < colCount; i++){
33862                         c = cs[i];
33863                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33864                         p.id = c.id;
33865                         p.css = p.attr = "";
33866                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33867                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33868                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33869                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33870                         }
33871                         var markup = ct.apply(p);
33872                         if(!c.locked){
33873                             cb+= markup;
33874                         }else{
33875                             lcb+= markup;
33876                         }
33877                     }
33878                     var alt = [];
33879                     if(stripe && ((rowIndex+1) % 2 == 0)){
33880                         alt.push("x-grid-row-alt")
33881                     }
33882                     if(r.dirty){
33883                         alt.push(  " x-grid-dirty-row");
33884                     }
33885                     rp.cells = lcb;
33886                     if(this.getRowClass){
33887                         alt.push(this.getRowClass(r, rowIndex));
33888                     }
33889                     if (hasListener) {
33890                         rowcfg = {
33891                              
33892                             record: r,
33893                             rowIndex : rowIndex,
33894                             rowClass : ''
33895                         }
33896                         this.grid.fireEvent('rowclass', this, rowcfg);
33897                         alt.push(rowcfg.rowClass);
33898                     }
33899                     rp.alt = alt.join(" ");
33900                     lbuf+= rt.apply(rp);
33901                     rp.cells = cb;
33902                     buf+=  rt.apply(rp);
33903                 }
33904                 return [lbuf, buf];
33905             } :
33906             function(cs, rs, ds, startRow, colCount, stripe){
33907                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33908                 // buffers
33909                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33910                 var hasListener = this.grid.hasListener('rowclass');
33911                 var rowcfg = {};
33912                 for(var j = 0, len = rs.length; j < len; j++){
33913                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33914                     for(var i = 0; i < colCount; i++){
33915                         c = cs[i];
33916                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33917                         p.id = c.id;
33918                         p.css = p.attr = "";
33919                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33920                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33921                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33922                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33923                         }
33924                         var markup = ct.apply(p);
33925                         if(!c.locked){
33926                             cb[cb.length] = markup;
33927                         }else{
33928                             lcb[lcb.length] = markup;
33929                         }
33930                     }
33931                     var alt = [];
33932                     if(stripe && ((rowIndex+1) % 2 == 0)){
33933                         alt.push( "x-grid-row-alt");
33934                     }
33935                     if(r.dirty){
33936                         alt.push(" x-grid-dirty-row");
33937                     }
33938                     rp.cells = lcb;
33939                     if(this.getRowClass){
33940                         alt.push( this.getRowClass(r, rowIndex));
33941                     }
33942                     if (hasListener) {
33943                         rowcfg = {
33944                              
33945                             record: r,
33946                             rowIndex : rowIndex,
33947                             rowClass : ''
33948                         }
33949                         this.grid.fireEvent('rowclass', this, rowcfg);
33950                         alt.push(rowcfg.rowClass);
33951                     }
33952                     rp.alt = alt.join(" ");
33953                     rp.cells = lcb.join("");
33954                     lbuf[lbuf.length] = rt.apply(rp);
33955                     rp.cells = cb.join("");
33956                     buf[buf.length] =  rt.apply(rp);
33957                 }
33958                 return [lbuf.join(""), buf.join("")];
33959             },
33960
33961     renderBody : function(){
33962         var markup = this.renderRows();
33963         var bt = this.templates.body;
33964         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33965     },
33966
33967     /**
33968      * Refreshes the grid
33969      * @param {Boolean} headersToo
33970      */
33971     refresh : function(headersToo){
33972         this.fireEvent("beforerefresh", this);
33973         this.grid.stopEditing();
33974         var result = this.renderBody();
33975         this.lockedBody.update(result[0]);
33976         this.mainBody.update(result[1]);
33977         if(headersToo === true){
33978             this.updateHeaders();
33979             this.updateColumns();
33980             this.updateSplitters();
33981             this.updateHeaderSortState();
33982         }
33983         this.syncRowHeights();
33984         this.layout();
33985         this.fireEvent("refresh", this);
33986     },
33987
33988     handleColumnMove : function(cm, oldIndex, newIndex){
33989         this.indexMap = null;
33990         var s = this.getScrollState();
33991         this.refresh(true);
33992         this.restoreScroll(s);
33993         this.afterMove(newIndex);
33994     },
33995
33996     afterMove : function(colIndex){
33997         if(this.enableMoveAnim && Roo.enableFx){
33998             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33999         }
34000         // if multisort - fix sortOrder, and reload..
34001         if (this.grid.dataSource.multiSort) {
34002             // the we can call sort again..
34003             var dm = this.grid.dataSource;
34004             var cm = this.grid.colModel;
34005             var so = [];
34006             for(var i = 0; i < cm.config.length; i++ ) {
34007                 
34008                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34009                     continue; // dont' bother, it's not in sort list or being set.
34010                 }
34011                 
34012                 so.push(cm.config[i].dataIndex);
34013             };
34014             dm.sortOrder = so;
34015             dm.load(dm.lastOptions);
34016             
34017             
34018         }
34019         
34020     },
34021
34022     updateCell : function(dm, rowIndex, dataIndex){
34023         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34024         if(typeof colIndex == "undefined"){ // not present in grid
34025             return;
34026         }
34027         var cm = this.grid.colModel;
34028         var cell = this.getCell(rowIndex, colIndex);
34029         var cellText = this.getCellText(rowIndex, colIndex);
34030
34031         var p = {
34032             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34033             id : cm.getColumnId(colIndex),
34034             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34035         };
34036         var renderer = cm.getRenderer(colIndex);
34037         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34038         if(typeof val == "undefined" || val === "") val = "&#160;";
34039         cellText.innerHTML = val;
34040         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34041         this.syncRowHeights(rowIndex, rowIndex);
34042     },
34043
34044     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34045         var maxWidth = 0;
34046         if(this.grid.autoSizeHeaders){
34047             var h = this.getHeaderCellMeasure(colIndex);
34048             maxWidth = Math.max(maxWidth, h.scrollWidth);
34049         }
34050         var tb, index;
34051         if(this.cm.isLocked(colIndex)){
34052             tb = this.getLockedTable();
34053             index = colIndex;
34054         }else{
34055             tb = this.getBodyTable();
34056             index = colIndex - this.cm.getLockedCount();
34057         }
34058         if(tb && tb.rows){
34059             var rows = tb.rows;
34060             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34061             for(var i = 0; i < stopIndex; i++){
34062                 var cell = rows[i].childNodes[index].firstChild;
34063                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34064             }
34065         }
34066         return maxWidth + /*margin for error in IE*/ 5;
34067     },
34068     /**
34069      * Autofit a column to its content.
34070      * @param {Number} colIndex
34071      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34072      */
34073      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34074          if(this.cm.isHidden(colIndex)){
34075              return; // can't calc a hidden column
34076          }
34077         if(forceMinSize){
34078             var cid = this.cm.getColumnId(colIndex);
34079             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34080            if(this.grid.autoSizeHeaders){
34081                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34082            }
34083         }
34084         var newWidth = this.calcColumnWidth(colIndex);
34085         this.cm.setColumnWidth(colIndex,
34086             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34087         if(!suppressEvent){
34088             this.grid.fireEvent("columnresize", colIndex, newWidth);
34089         }
34090     },
34091
34092     /**
34093      * Autofits all columns to their content and then expands to fit any extra space in the grid
34094      */
34095      autoSizeColumns : function(){
34096         var cm = this.grid.colModel;
34097         var colCount = cm.getColumnCount();
34098         for(var i = 0; i < colCount; i++){
34099             this.autoSizeColumn(i, true, true);
34100         }
34101         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34102             this.fitColumns();
34103         }else{
34104             this.updateColumns();
34105             this.layout();
34106         }
34107     },
34108
34109     /**
34110      * Autofits all columns to the grid's width proportionate with their current size
34111      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34112      */
34113     fitColumns : function(reserveScrollSpace){
34114         var cm = this.grid.colModel;
34115         var colCount = cm.getColumnCount();
34116         var cols = [];
34117         var width = 0;
34118         var i, w;
34119         for (i = 0; i < colCount; i++){
34120             if(!cm.isHidden(i) && !cm.isFixed(i)){
34121                 w = cm.getColumnWidth(i);
34122                 cols.push(i);
34123                 cols.push(w);
34124                 width += w;
34125             }
34126         }
34127         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34128         if(reserveScrollSpace){
34129             avail -= 17;
34130         }
34131         var frac = (avail - cm.getTotalWidth())/width;
34132         while (cols.length){
34133             w = cols.pop();
34134             i = cols.pop();
34135             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34136         }
34137         this.updateColumns();
34138         this.layout();
34139     },
34140
34141     onRowSelect : function(rowIndex){
34142         var row = this.getRowComposite(rowIndex);
34143         row.addClass("x-grid-row-selected");
34144     },
34145
34146     onRowDeselect : function(rowIndex){
34147         var row = this.getRowComposite(rowIndex);
34148         row.removeClass("x-grid-row-selected");
34149     },
34150
34151     onCellSelect : function(row, col){
34152         var cell = this.getCell(row, col);
34153         if(cell){
34154             Roo.fly(cell).addClass("x-grid-cell-selected");
34155         }
34156     },
34157
34158     onCellDeselect : function(row, col){
34159         var cell = this.getCell(row, col);
34160         if(cell){
34161             Roo.fly(cell).removeClass("x-grid-cell-selected");
34162         }
34163     },
34164
34165     updateHeaderSortState : function(){
34166         
34167         // sort state can be single { field: xxx, direction : yyy}
34168         // or   { xxx=>ASC , yyy : DESC ..... }
34169         
34170         var mstate = {};
34171         if (!this.ds.multiSort) { 
34172             var state = this.ds.getSortState();
34173             if(!state){
34174                 return;
34175             }
34176             mstate[state.field] = state.direction;
34177             // FIXME... - this is not used here.. but might be elsewhere..
34178             this.sortState = state;
34179             
34180         } else {
34181             mstate = this.ds.sortToggle;
34182         }
34183         //remove existing sort classes..
34184         
34185         var sc = this.sortClasses;
34186         var hds = this.el.select(this.headerSelector).removeClass(sc);
34187         
34188         for(var f in mstate) {
34189         
34190             var sortColumn = this.cm.findColumnIndex(f);
34191             
34192             if(sortColumn != -1){
34193                 var sortDir = mstate[f];        
34194                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34195             }
34196         }
34197         
34198          
34199         
34200     },
34201
34202
34203     handleHeaderClick : function(g, index){
34204         if(this.headersDisabled){
34205             return;
34206         }
34207         var dm = g.dataSource, cm = g.colModel;
34208         if(!cm.isSortable(index)){
34209             return;
34210         }
34211         g.stopEditing();
34212         
34213         if (dm.multiSort) {
34214             // update the sortOrder
34215             var so = [];
34216             for(var i = 0; i < cm.config.length; i++ ) {
34217                 
34218                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34219                     continue; // dont' bother, it's not in sort list or being set.
34220                 }
34221                 
34222                 so.push(cm.config[i].dataIndex);
34223             };
34224             dm.sortOrder = so;
34225         }
34226         
34227         
34228         dm.sort(cm.getDataIndex(index));
34229     },
34230
34231
34232     destroy : function(){
34233         if(this.colMenu){
34234             this.colMenu.removeAll();
34235             Roo.menu.MenuMgr.unregister(this.colMenu);
34236             this.colMenu.getEl().remove();
34237             delete this.colMenu;
34238         }
34239         if(this.hmenu){
34240             this.hmenu.removeAll();
34241             Roo.menu.MenuMgr.unregister(this.hmenu);
34242             this.hmenu.getEl().remove();
34243             delete this.hmenu;
34244         }
34245         if(this.grid.enableColumnMove){
34246             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34247             if(dds){
34248                 for(var dd in dds){
34249                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34250                         var elid = dds[dd].dragElId;
34251                         dds[dd].unreg();
34252                         Roo.get(elid).remove();
34253                     } else if(dds[dd].config.isTarget){
34254                         dds[dd].proxyTop.remove();
34255                         dds[dd].proxyBottom.remove();
34256                         dds[dd].unreg();
34257                     }
34258                     if(Roo.dd.DDM.locationCache[dd]){
34259                         delete Roo.dd.DDM.locationCache[dd];
34260                     }
34261                 }
34262                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34263             }
34264         }
34265         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34266         this.bind(null, null);
34267         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34268     },
34269
34270     handleLockChange : function(){
34271         this.refresh(true);
34272     },
34273
34274     onDenyColumnLock : function(){
34275
34276     },
34277
34278     onDenyColumnHide : function(){
34279
34280     },
34281
34282     handleHdMenuClick : function(item){
34283         var index = this.hdCtxIndex;
34284         var cm = this.cm, ds = this.ds;
34285         switch(item.id){
34286             case "asc":
34287                 ds.sort(cm.getDataIndex(index), "ASC");
34288                 break;
34289             case "desc":
34290                 ds.sort(cm.getDataIndex(index), "DESC");
34291                 break;
34292             case "lock":
34293                 var lc = cm.getLockedCount();
34294                 if(cm.getColumnCount(true) <= lc+1){
34295                     this.onDenyColumnLock();
34296                     return;
34297                 }
34298                 if(lc != index){
34299                     cm.setLocked(index, true, true);
34300                     cm.moveColumn(index, lc);
34301                     this.grid.fireEvent("columnmove", index, lc);
34302                 }else{
34303                     cm.setLocked(index, true);
34304                 }
34305             break;
34306             case "unlock":
34307                 var lc = cm.getLockedCount();
34308                 if((lc-1) != index){
34309                     cm.setLocked(index, false, true);
34310                     cm.moveColumn(index, lc-1);
34311                     this.grid.fireEvent("columnmove", index, lc-1);
34312                 }else{
34313                     cm.setLocked(index, false);
34314                 }
34315             break;
34316             default:
34317                 index = cm.getIndexById(item.id.substr(4));
34318                 if(index != -1){
34319                     if(item.checked && cm.getColumnCount(true) <= 1){
34320                         this.onDenyColumnHide();
34321                         return false;
34322                     }
34323                     cm.setHidden(index, item.checked);
34324                 }
34325         }
34326         return true;
34327     },
34328
34329     beforeColMenuShow : function(){
34330         var cm = this.cm,  colCount = cm.getColumnCount();
34331         this.colMenu.removeAll();
34332         for(var i = 0; i < colCount; i++){
34333             this.colMenu.add(new Roo.menu.CheckItem({
34334                 id: "col-"+cm.getColumnId(i),
34335                 text: cm.getColumnHeader(i),
34336                 checked: !cm.isHidden(i),
34337                 hideOnClick:false
34338             }));
34339         }
34340     },
34341
34342     handleHdCtx : function(g, index, e){
34343         e.stopEvent();
34344         var hd = this.getHeaderCell(index);
34345         this.hdCtxIndex = index;
34346         var ms = this.hmenu.items, cm = this.cm;
34347         ms.get("asc").setDisabled(!cm.isSortable(index));
34348         ms.get("desc").setDisabled(!cm.isSortable(index));
34349         if(this.grid.enableColLock !== false){
34350             ms.get("lock").setDisabled(cm.isLocked(index));
34351             ms.get("unlock").setDisabled(!cm.isLocked(index));
34352         }
34353         this.hmenu.show(hd, "tl-bl");
34354     },
34355
34356     handleHdOver : function(e){
34357         var hd = this.findHeaderCell(e.getTarget());
34358         if(hd && !this.headersDisabled){
34359             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34360                this.fly(hd).addClass("x-grid-hd-over");
34361             }
34362         }
34363     },
34364
34365     handleHdOut : function(e){
34366         var hd = this.findHeaderCell(e.getTarget());
34367         if(hd){
34368             this.fly(hd).removeClass("x-grid-hd-over");
34369         }
34370     },
34371
34372     handleSplitDblClick : function(e, t){
34373         var i = this.getCellIndex(t);
34374         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34375             this.autoSizeColumn(i, true);
34376             this.layout();
34377         }
34378     },
34379
34380     render : function(){
34381
34382         var cm = this.cm;
34383         var colCount = cm.getColumnCount();
34384
34385         if(this.grid.monitorWindowResize === true){
34386             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34387         }
34388         var header = this.renderHeaders();
34389         var body = this.templates.body.apply({rows:""});
34390         var html = this.templates.master.apply({
34391             lockedBody: body,
34392             body: body,
34393             lockedHeader: header[0],
34394             header: header[1]
34395         });
34396
34397         //this.updateColumns();
34398
34399         this.grid.getGridEl().dom.innerHTML = html;
34400
34401         this.initElements();
34402         
34403         // a kludge to fix the random scolling effect in webkit
34404         this.el.on("scroll", function() {
34405             this.el.dom.scrollTop=0; // hopefully not recursive..
34406         },this);
34407
34408         this.scroller.on("scroll", this.handleScroll, this);
34409         this.lockedBody.on("mousewheel", this.handleWheel, this);
34410         this.mainBody.on("mousewheel", this.handleWheel, this);
34411
34412         this.mainHd.on("mouseover", this.handleHdOver, this);
34413         this.mainHd.on("mouseout", this.handleHdOut, this);
34414         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34415                 {delegate: "."+this.splitClass});
34416
34417         this.lockedHd.on("mouseover", this.handleHdOver, this);
34418         this.lockedHd.on("mouseout", this.handleHdOut, this);
34419         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34420                 {delegate: "."+this.splitClass});
34421
34422         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34423             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34424         }
34425
34426         this.updateSplitters();
34427
34428         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34429             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34430             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34431         }
34432
34433         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34434             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34435             this.hmenu.add(
34436                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34437                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34438             );
34439             if(this.grid.enableColLock !== false){
34440                 this.hmenu.add('-',
34441                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34442                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34443                 );
34444             }
34445             if(this.grid.enableColumnHide !== false){
34446
34447                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34448                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34449                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34450
34451                 this.hmenu.add('-',
34452                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34453                 );
34454             }
34455             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34456
34457             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34458         }
34459
34460         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34461             this.dd = new Roo.grid.GridDragZone(this.grid, {
34462                 ddGroup : this.grid.ddGroup || 'GridDD'
34463             });
34464         }
34465
34466         /*
34467         for(var i = 0; i < colCount; i++){
34468             if(cm.isHidden(i)){
34469                 this.hideColumn(i);
34470             }
34471             if(cm.config[i].align){
34472                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34473                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34474             }
34475         }*/
34476         
34477         this.updateHeaderSortState();
34478
34479         this.beforeInitialResize();
34480         this.layout(true);
34481
34482         // two part rendering gives faster view to the user
34483         this.renderPhase2.defer(1, this);
34484     },
34485
34486     renderPhase2 : function(){
34487         // render the rows now
34488         this.refresh();
34489         if(this.grid.autoSizeColumns){
34490             this.autoSizeColumns();
34491         }
34492     },
34493
34494     beforeInitialResize : function(){
34495
34496     },
34497
34498     onColumnSplitterMoved : function(i, w){
34499         this.userResized = true;
34500         var cm = this.grid.colModel;
34501         cm.setColumnWidth(i, w, true);
34502         var cid = cm.getColumnId(i);
34503         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34504         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34505         this.updateSplitters();
34506         this.layout();
34507         this.grid.fireEvent("columnresize", i, w);
34508     },
34509
34510     syncRowHeights : function(startIndex, endIndex){
34511         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34512             startIndex = startIndex || 0;
34513             var mrows = this.getBodyTable().rows;
34514             var lrows = this.getLockedTable().rows;
34515             var len = mrows.length-1;
34516             endIndex = Math.min(endIndex || len, len);
34517             for(var i = startIndex; i <= endIndex; i++){
34518                 var m = mrows[i], l = lrows[i];
34519                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34520                 m.style.height = l.style.height = h + "px";
34521             }
34522         }
34523     },
34524
34525     layout : function(initialRender, is2ndPass){
34526         var g = this.grid;
34527         var auto = g.autoHeight;
34528         var scrollOffset = 16;
34529         var c = g.getGridEl(), cm = this.cm,
34530                 expandCol = g.autoExpandColumn,
34531                 gv = this;
34532         //c.beginMeasure();
34533
34534         if(!c.dom.offsetWidth){ // display:none?
34535             if(initialRender){
34536                 this.lockedWrap.show();
34537                 this.mainWrap.show();
34538             }
34539             return;
34540         }
34541
34542         var hasLock = this.cm.isLocked(0);
34543
34544         var tbh = this.headerPanel.getHeight();
34545         var bbh = this.footerPanel.getHeight();
34546
34547         if(auto){
34548             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34549             var newHeight = ch + c.getBorderWidth("tb");
34550             if(g.maxHeight){
34551                 newHeight = Math.min(g.maxHeight, newHeight);
34552             }
34553             c.setHeight(newHeight);
34554         }
34555
34556         if(g.autoWidth){
34557             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34558         }
34559
34560         var s = this.scroller;
34561
34562         var csize = c.getSize(true);
34563
34564         this.el.setSize(csize.width, csize.height);
34565
34566         this.headerPanel.setWidth(csize.width);
34567         this.footerPanel.setWidth(csize.width);
34568
34569         var hdHeight = this.mainHd.getHeight();
34570         var vw = csize.width;
34571         var vh = csize.height - (tbh + bbh);
34572
34573         s.setSize(vw, vh);
34574
34575         var bt = this.getBodyTable();
34576         var ltWidth = hasLock ?
34577                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34578
34579         var scrollHeight = bt.offsetHeight;
34580         var scrollWidth = ltWidth + bt.offsetWidth;
34581         var vscroll = false, hscroll = false;
34582
34583         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34584
34585         var lw = this.lockedWrap, mw = this.mainWrap;
34586         var lb = this.lockedBody, mb = this.mainBody;
34587
34588         setTimeout(function(){
34589             var t = s.dom.offsetTop;
34590             var w = s.dom.clientWidth,
34591                 h = s.dom.clientHeight;
34592
34593             lw.setTop(t);
34594             lw.setSize(ltWidth, h);
34595
34596             mw.setLeftTop(ltWidth, t);
34597             mw.setSize(w-ltWidth, h);
34598
34599             lb.setHeight(h-hdHeight);
34600             mb.setHeight(h-hdHeight);
34601
34602             if(is2ndPass !== true && !gv.userResized && expandCol){
34603                 // high speed resize without full column calculation
34604                 
34605                 var ci = cm.getIndexById(expandCol);
34606                 if (ci < 0) {
34607                     ci = cm.findColumnIndex(expandCol);
34608                 }
34609                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34610                 var expandId = cm.getColumnId(ci);
34611                 var  tw = cm.getTotalWidth(false);
34612                 var currentWidth = cm.getColumnWidth(ci);
34613                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34614                 if(currentWidth != cw){
34615                     cm.setColumnWidth(ci, cw, true);
34616                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34617                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34618                     gv.updateSplitters();
34619                     gv.layout(false, true);
34620                 }
34621             }
34622
34623             if(initialRender){
34624                 lw.show();
34625                 mw.show();
34626             }
34627             //c.endMeasure();
34628         }, 10);
34629     },
34630
34631     onWindowResize : function(){
34632         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34633             return;
34634         }
34635         this.layout();
34636     },
34637
34638     appendFooter : function(parentEl){
34639         return null;
34640     },
34641
34642     sortAscText : "Sort Ascending",
34643     sortDescText : "Sort Descending",
34644     lockText : "Lock Column",
34645     unlockText : "Unlock Column",
34646     columnsText : "Columns"
34647 });
34648
34649
34650 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34651     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34652     this.proxy.el.addClass('x-grid3-col-dd');
34653 };
34654
34655 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34656     handleMouseDown : function(e){
34657
34658     },
34659
34660     callHandleMouseDown : function(e){
34661         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34662     }
34663 });
34664 /*
34665  * Based on:
34666  * Ext JS Library 1.1.1
34667  * Copyright(c) 2006-2007, Ext JS, LLC.
34668  *
34669  * Originally Released Under LGPL - original licence link has changed is not relivant.
34670  *
34671  * Fork - LGPL
34672  * <script type="text/javascript">
34673  */
34674  
34675 // private
34676 // This is a support class used internally by the Grid components
34677 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34678     this.grid = grid;
34679     this.view = grid.getView();
34680     this.proxy = this.view.resizeProxy;
34681     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34682         "gridSplitters" + this.grid.getGridEl().id, {
34683         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34684     });
34685     this.setHandleElId(Roo.id(hd));
34686     this.setOuterHandleElId(Roo.id(hd2));
34687     this.scroll = false;
34688 };
34689 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34690     fly: Roo.Element.fly,
34691
34692     b4StartDrag : function(x, y){
34693         this.view.headersDisabled = true;
34694         this.proxy.setHeight(this.view.mainWrap.getHeight());
34695         var w = this.cm.getColumnWidth(this.cellIndex);
34696         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34697         this.resetConstraints();
34698         this.setXConstraint(minw, 1000);
34699         this.setYConstraint(0, 0);
34700         this.minX = x - minw;
34701         this.maxX = x + 1000;
34702         this.startPos = x;
34703         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34704     },
34705
34706
34707     handleMouseDown : function(e){
34708         ev = Roo.EventObject.setEvent(e);
34709         var t = this.fly(ev.getTarget());
34710         if(t.hasClass("x-grid-split")){
34711             this.cellIndex = this.view.getCellIndex(t.dom);
34712             this.split = t.dom;
34713             this.cm = this.grid.colModel;
34714             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34715                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34716             }
34717         }
34718     },
34719
34720     endDrag : function(e){
34721         this.view.headersDisabled = false;
34722         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34723         var diff = endX - this.startPos;
34724         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34725     },
34726
34727     autoOffset : function(){
34728         this.setDelta(0,0);
34729     }
34730 });/*
34731  * Based on:
34732  * Ext JS Library 1.1.1
34733  * Copyright(c) 2006-2007, Ext JS, LLC.
34734  *
34735  * Originally Released Under LGPL - original licence link has changed is not relivant.
34736  *
34737  * Fork - LGPL
34738  * <script type="text/javascript">
34739  */
34740  
34741 // private
34742 // This is a support class used internally by the Grid components
34743 Roo.grid.GridDragZone = function(grid, config){
34744     this.view = grid.getView();
34745     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34746     if(this.view.lockedBody){
34747         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34748         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34749     }
34750     this.scroll = false;
34751     this.grid = grid;
34752     this.ddel = document.createElement('div');
34753     this.ddel.className = 'x-grid-dd-wrap';
34754 };
34755
34756 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34757     ddGroup : "GridDD",
34758
34759     getDragData : function(e){
34760         var t = Roo.lib.Event.getTarget(e);
34761         var rowIndex = this.view.findRowIndex(t);
34762         if(rowIndex !== false){
34763             var sm = this.grid.selModel;
34764             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34765               //  sm.mouseDown(e, t);
34766             //}
34767             if (e.hasModifier()){
34768                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34769             }
34770             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34771         }
34772         return false;
34773     },
34774
34775     onInitDrag : function(e){
34776         var data = this.dragData;
34777         this.ddel.innerHTML = this.grid.getDragDropText();
34778         this.proxy.update(this.ddel);
34779         // fire start drag?
34780     },
34781
34782     afterRepair : function(){
34783         this.dragging = false;
34784     },
34785
34786     getRepairXY : function(e, data){
34787         return false;
34788     },
34789
34790     onEndDrag : function(data, e){
34791         // fire end drag?
34792     },
34793
34794     onValidDrop : function(dd, e, id){
34795         // fire drag drop?
34796         this.hideProxy();
34797     },
34798
34799     beforeInvalidDrop : function(e, id){
34800
34801     }
34802 });/*
34803  * Based on:
34804  * Ext JS Library 1.1.1
34805  * Copyright(c) 2006-2007, Ext JS, LLC.
34806  *
34807  * Originally Released Under LGPL - original licence link has changed is not relivant.
34808  *
34809  * Fork - LGPL
34810  * <script type="text/javascript">
34811  */
34812  
34813
34814 /**
34815  * @class Roo.grid.ColumnModel
34816  * @extends Roo.util.Observable
34817  * This is the default implementation of a ColumnModel used by the Grid. It defines
34818  * the columns in the grid.
34819  * <br>Usage:<br>
34820  <pre><code>
34821  var colModel = new Roo.grid.ColumnModel([
34822         {header: "Ticker", width: 60, sortable: true, locked: true},
34823         {header: "Company Name", width: 150, sortable: true},
34824         {header: "Market Cap.", width: 100, sortable: true},
34825         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34826         {header: "Employees", width: 100, sortable: true, resizable: false}
34827  ]);
34828  </code></pre>
34829  * <p>
34830  
34831  * The config options listed for this class are options which may appear in each
34832  * individual column definition.
34833  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34834  * @constructor
34835  * @param {Object} config An Array of column config objects. See this class's
34836  * config objects for details.
34837 */
34838 Roo.grid.ColumnModel = function(config){
34839         /**
34840      * The config passed into the constructor
34841      */
34842     this.config = config;
34843     this.lookup = {};
34844
34845     // if no id, create one
34846     // if the column does not have a dataIndex mapping,
34847     // map it to the order it is in the config
34848     for(var i = 0, len = config.length; i < len; i++){
34849         var c = config[i];
34850         if(typeof c.dataIndex == "undefined"){
34851             c.dataIndex = i;
34852         }
34853         if(typeof c.renderer == "string"){
34854             c.renderer = Roo.util.Format[c.renderer];
34855         }
34856         if(typeof c.id == "undefined"){
34857             c.id = Roo.id();
34858         }
34859         if(c.editor && c.editor.xtype){
34860             c.editor  = Roo.factory(c.editor, Roo.grid);
34861         }
34862         if(c.editor && c.editor.isFormField){
34863             c.editor = new Roo.grid.GridEditor(c.editor);
34864         }
34865         this.lookup[c.id] = c;
34866     }
34867
34868     /**
34869      * The width of columns which have no width specified (defaults to 100)
34870      * @type Number
34871      */
34872     this.defaultWidth = 100;
34873
34874     /**
34875      * Default sortable of columns which have no sortable specified (defaults to false)
34876      * @type Boolean
34877      */
34878     this.defaultSortable = false;
34879
34880     this.addEvents({
34881         /**
34882              * @event widthchange
34883              * Fires when the width of a column changes.
34884              * @param {ColumnModel} this
34885              * @param {Number} columnIndex The column index
34886              * @param {Number} newWidth The new width
34887              */
34888             "widthchange": true,
34889         /**
34890              * @event headerchange
34891              * Fires when the text of a header changes.
34892              * @param {ColumnModel} this
34893              * @param {Number} columnIndex The column index
34894              * @param {Number} newText The new header text
34895              */
34896             "headerchange": true,
34897         /**
34898              * @event hiddenchange
34899              * Fires when a column is hidden or "unhidden".
34900              * @param {ColumnModel} this
34901              * @param {Number} columnIndex The column index
34902              * @param {Boolean} hidden true if hidden, false otherwise
34903              */
34904             "hiddenchange": true,
34905             /**
34906          * @event columnmoved
34907          * Fires when a column is moved.
34908          * @param {ColumnModel} this
34909          * @param {Number} oldIndex
34910          * @param {Number} newIndex
34911          */
34912         "columnmoved" : true,
34913         /**
34914          * @event columlockchange
34915          * Fires when a column's locked state is changed
34916          * @param {ColumnModel} this
34917          * @param {Number} colIndex
34918          * @param {Boolean} locked true if locked
34919          */
34920         "columnlockchange" : true
34921     });
34922     Roo.grid.ColumnModel.superclass.constructor.call(this);
34923 };
34924 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34925     /**
34926      * @cfg {String} header The header text to display in the Grid view.
34927      */
34928     /**
34929      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34930      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34931      * specified, the column's index is used as an index into the Record's data Array.
34932      */
34933     /**
34934      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34935      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34936      */
34937     /**
34938      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34939      * Defaults to the value of the {@link #defaultSortable} property.
34940      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34941      */
34942     /**
34943      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34944      */
34945     /**
34946      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34947      */
34948     /**
34949      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34950      */
34951     /**
34952      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34953      */
34954     /**
34955      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34956      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34957      * default renderer uses the raw data value.
34958      */
34959        /**
34960      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34961      */
34962     /**
34963      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34964      */
34965
34966     /**
34967      * Returns the id of the column at the specified index.
34968      * @param {Number} index The column index
34969      * @return {String} the id
34970      */
34971     getColumnId : function(index){
34972         return this.config[index].id;
34973     },
34974
34975     /**
34976      * Returns the column for a specified id.
34977      * @param {String} id The column id
34978      * @return {Object} the column
34979      */
34980     getColumnById : function(id){
34981         return this.lookup[id];
34982     },
34983
34984     
34985     /**
34986      * Returns the column for a specified dataIndex.
34987      * @param {String} dataIndex The column dataIndex
34988      * @return {Object|Boolean} the column or false if not found
34989      */
34990     getColumnByDataIndex: function(dataIndex){
34991         var index = this.findColumnIndex(dataIndex);
34992         return index > -1 ? this.config[index] : false;
34993     },
34994     
34995     /**
34996      * Returns the index for a specified column id.
34997      * @param {String} id The column id
34998      * @return {Number} the index, or -1 if not found
34999      */
35000     getIndexById : function(id){
35001         for(var i = 0, len = this.config.length; i < len; i++){
35002             if(this.config[i].id == id){
35003                 return i;
35004             }
35005         }
35006         return -1;
35007     },
35008     
35009     /**
35010      * Returns the index for a specified column dataIndex.
35011      * @param {String} dataIndex The column dataIndex
35012      * @return {Number} the index, or -1 if not found
35013      */
35014     
35015     findColumnIndex : function(dataIndex){
35016         for(var i = 0, len = this.config.length; i < len; i++){
35017             if(this.config[i].dataIndex == dataIndex){
35018                 return i;
35019             }
35020         }
35021         return -1;
35022     },
35023     
35024     
35025     moveColumn : function(oldIndex, newIndex){
35026         var c = this.config[oldIndex];
35027         this.config.splice(oldIndex, 1);
35028         this.config.splice(newIndex, 0, c);
35029         this.dataMap = null;
35030         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35031     },
35032
35033     isLocked : function(colIndex){
35034         return this.config[colIndex].locked === true;
35035     },
35036
35037     setLocked : function(colIndex, value, suppressEvent){
35038         if(this.isLocked(colIndex) == value){
35039             return;
35040         }
35041         this.config[colIndex].locked = value;
35042         if(!suppressEvent){
35043             this.fireEvent("columnlockchange", this, colIndex, value);
35044         }
35045     },
35046
35047     getTotalLockedWidth : function(){
35048         var totalWidth = 0;
35049         for(var i = 0; i < this.config.length; i++){
35050             if(this.isLocked(i) && !this.isHidden(i)){
35051                 this.totalWidth += this.getColumnWidth(i);
35052             }
35053         }
35054         return totalWidth;
35055     },
35056
35057     getLockedCount : function(){
35058         for(var i = 0, len = this.config.length; i < len; i++){
35059             if(!this.isLocked(i)){
35060                 return i;
35061             }
35062         }
35063     },
35064
35065     /**
35066      * Returns the number of columns.
35067      * @return {Number}
35068      */
35069     getColumnCount : function(visibleOnly){
35070         if(visibleOnly === true){
35071             var c = 0;
35072             for(var i = 0, len = this.config.length; i < len; i++){
35073                 if(!this.isHidden(i)){
35074                     c++;
35075                 }
35076             }
35077             return c;
35078         }
35079         return this.config.length;
35080     },
35081
35082     /**
35083      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35084      * @param {Function} fn
35085      * @param {Object} scope (optional)
35086      * @return {Array} result
35087      */
35088     getColumnsBy : function(fn, scope){
35089         var r = [];
35090         for(var i = 0, len = this.config.length; i < len; i++){
35091             var c = this.config[i];
35092             if(fn.call(scope||this, c, i) === true){
35093                 r[r.length] = c;
35094             }
35095         }
35096         return r;
35097     },
35098
35099     /**
35100      * Returns true if the specified column is sortable.
35101      * @param {Number} col The column index
35102      * @return {Boolean}
35103      */
35104     isSortable : function(col){
35105         if(typeof this.config[col].sortable == "undefined"){
35106             return this.defaultSortable;
35107         }
35108         return this.config[col].sortable;
35109     },
35110
35111     /**
35112      * Returns the rendering (formatting) function defined for the column.
35113      * @param {Number} col The column index.
35114      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35115      */
35116     getRenderer : function(col){
35117         if(!this.config[col].renderer){
35118             return Roo.grid.ColumnModel.defaultRenderer;
35119         }
35120         return this.config[col].renderer;
35121     },
35122
35123     /**
35124      * Sets the rendering (formatting) function for a column.
35125      * @param {Number} col The column index
35126      * @param {Function} fn The function to use to process the cell's raw data
35127      * to return HTML markup for the grid view. The render function is called with
35128      * the following parameters:<ul>
35129      * <li>Data value.</li>
35130      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35131      * <li>css A CSS style string to apply to the table cell.</li>
35132      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35133      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35134      * <li>Row index</li>
35135      * <li>Column index</li>
35136      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35137      */
35138     setRenderer : function(col, fn){
35139         this.config[col].renderer = fn;
35140     },
35141
35142     /**
35143      * Returns the width for the specified column.
35144      * @param {Number} col The column index
35145      * @return {Number}
35146      */
35147     getColumnWidth : function(col){
35148         return this.config[col].width * 1 || this.defaultWidth;
35149     },
35150
35151     /**
35152      * Sets the width for a column.
35153      * @param {Number} col The column index
35154      * @param {Number} width The new width
35155      */
35156     setColumnWidth : function(col, width, suppressEvent){
35157         this.config[col].width = width;
35158         this.totalWidth = null;
35159         if(!suppressEvent){
35160              this.fireEvent("widthchange", this, col, width);
35161         }
35162     },
35163
35164     /**
35165      * Returns the total width of all columns.
35166      * @param {Boolean} includeHidden True to include hidden column widths
35167      * @return {Number}
35168      */
35169     getTotalWidth : function(includeHidden){
35170         if(!this.totalWidth){
35171             this.totalWidth = 0;
35172             for(var i = 0, len = this.config.length; i < len; i++){
35173                 if(includeHidden || !this.isHidden(i)){
35174                     this.totalWidth += this.getColumnWidth(i);
35175                 }
35176             }
35177         }
35178         return this.totalWidth;
35179     },
35180
35181     /**
35182      * Returns the header for the specified column.
35183      * @param {Number} col The column index
35184      * @return {String}
35185      */
35186     getColumnHeader : function(col){
35187         return this.config[col].header;
35188     },
35189
35190     /**
35191      * Sets the header for a column.
35192      * @param {Number} col The column index
35193      * @param {String} header The new header
35194      */
35195     setColumnHeader : function(col, header){
35196         this.config[col].header = header;
35197         this.fireEvent("headerchange", this, col, header);
35198     },
35199
35200     /**
35201      * Returns the tooltip for the specified column.
35202      * @param {Number} col The column index
35203      * @return {String}
35204      */
35205     getColumnTooltip : function(col){
35206             return this.config[col].tooltip;
35207     },
35208     /**
35209      * Sets the tooltip for a column.
35210      * @param {Number} col The column index
35211      * @param {String} tooltip The new tooltip
35212      */
35213     setColumnTooltip : function(col, tooltip){
35214             this.config[col].tooltip = tooltip;
35215     },
35216
35217     /**
35218      * Returns the dataIndex for the specified column.
35219      * @param {Number} col The column index
35220      * @return {Number}
35221      */
35222     getDataIndex : function(col){
35223         return this.config[col].dataIndex;
35224     },
35225
35226     /**
35227      * Sets the dataIndex for a column.
35228      * @param {Number} col The column index
35229      * @param {Number} dataIndex The new dataIndex
35230      */
35231     setDataIndex : function(col, dataIndex){
35232         this.config[col].dataIndex = dataIndex;
35233     },
35234
35235     
35236     
35237     /**
35238      * Returns true if the cell is editable.
35239      * @param {Number} colIndex The column index
35240      * @param {Number} rowIndex The row index
35241      * @return {Boolean}
35242      */
35243     isCellEditable : function(colIndex, rowIndex){
35244         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35245     },
35246
35247     /**
35248      * Returns the editor defined for the cell/column.
35249      * return false or null to disable editing.
35250      * @param {Number} colIndex The column index
35251      * @param {Number} rowIndex The row index
35252      * @return {Object}
35253      */
35254     getCellEditor : function(colIndex, rowIndex){
35255         return this.config[colIndex].editor;
35256     },
35257
35258     /**
35259      * Sets if a column is editable.
35260      * @param {Number} col The column index
35261      * @param {Boolean} editable True if the column is editable
35262      */
35263     setEditable : function(col, editable){
35264         this.config[col].editable = editable;
35265     },
35266
35267
35268     /**
35269      * Returns true if the column is hidden.
35270      * @param {Number} colIndex The column index
35271      * @return {Boolean}
35272      */
35273     isHidden : function(colIndex){
35274         return this.config[colIndex].hidden;
35275     },
35276
35277
35278     /**
35279      * Returns true if the column width cannot be changed
35280      */
35281     isFixed : function(colIndex){
35282         return this.config[colIndex].fixed;
35283     },
35284
35285     /**
35286      * Returns true if the column can be resized
35287      * @return {Boolean}
35288      */
35289     isResizable : function(colIndex){
35290         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35291     },
35292     /**
35293      * Sets if a column is hidden.
35294      * @param {Number} colIndex The column index
35295      * @param {Boolean} hidden True if the column is hidden
35296      */
35297     setHidden : function(colIndex, hidden){
35298         this.config[colIndex].hidden = hidden;
35299         this.totalWidth = null;
35300         this.fireEvent("hiddenchange", this, colIndex, hidden);
35301     },
35302
35303     /**
35304      * Sets the editor for a column.
35305      * @param {Number} col The column index
35306      * @param {Object} editor The editor object
35307      */
35308     setEditor : function(col, editor){
35309         this.config[col].editor = editor;
35310     }
35311 });
35312
35313 Roo.grid.ColumnModel.defaultRenderer = function(value){
35314         if(typeof value == "string" && value.length < 1){
35315             return "&#160;";
35316         }
35317         return value;
35318 };
35319
35320 // Alias for backwards compatibility
35321 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35322 /*
35323  * Based on:
35324  * Ext JS Library 1.1.1
35325  * Copyright(c) 2006-2007, Ext JS, LLC.
35326  *
35327  * Originally Released Under LGPL - original licence link has changed is not relivant.
35328  *
35329  * Fork - LGPL
35330  * <script type="text/javascript">
35331  */
35332
35333 /**
35334  * @class Roo.grid.AbstractSelectionModel
35335  * @extends Roo.util.Observable
35336  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35337  * implemented by descendant classes.  This class should not be directly instantiated.
35338  * @constructor
35339  */
35340 Roo.grid.AbstractSelectionModel = function(){
35341     this.locked = false;
35342     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35343 };
35344
35345 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35346     /** @ignore Called by the grid automatically. Do not call directly. */
35347     init : function(grid){
35348         this.grid = grid;
35349         this.initEvents();
35350     },
35351
35352     /**
35353      * Locks the selections.
35354      */
35355     lock : function(){
35356         this.locked = true;
35357     },
35358
35359     /**
35360      * Unlocks the selections.
35361      */
35362     unlock : function(){
35363         this.locked = false;
35364     },
35365
35366     /**
35367      * Returns true if the selections are locked.
35368      * @return {Boolean}
35369      */
35370     isLocked : function(){
35371         return this.locked;
35372     }
35373 });/*
35374  * Based on:
35375  * Ext JS Library 1.1.1
35376  * Copyright(c) 2006-2007, Ext JS, LLC.
35377  *
35378  * Originally Released Under LGPL - original licence link has changed is not relivant.
35379  *
35380  * Fork - LGPL
35381  * <script type="text/javascript">
35382  */
35383 /**
35384  * @extends Roo.grid.AbstractSelectionModel
35385  * @class Roo.grid.RowSelectionModel
35386  * The default SelectionModel used by {@link Roo.grid.Grid}.
35387  * It supports multiple selections and keyboard selection/navigation. 
35388  * @constructor
35389  * @param {Object} config
35390  */
35391 Roo.grid.RowSelectionModel = function(config){
35392     Roo.apply(this, config);
35393     this.selections = new Roo.util.MixedCollection(false, function(o){
35394         return o.id;
35395     });
35396
35397     this.last = false;
35398     this.lastActive = false;
35399
35400     this.addEvents({
35401         /**
35402              * @event selectionchange
35403              * Fires when the selection changes
35404              * @param {SelectionModel} this
35405              */
35406             "selectionchange" : true,
35407         /**
35408              * @event afterselectionchange
35409              * Fires after the selection changes (eg. by key press or clicking)
35410              * @param {SelectionModel} this
35411              */
35412             "afterselectionchange" : true,
35413         /**
35414              * @event beforerowselect
35415              * Fires when a row is selected being selected, return false to cancel.
35416              * @param {SelectionModel} this
35417              * @param {Number} rowIndex The selected index
35418              * @param {Boolean} keepExisting False if other selections will be cleared
35419              */
35420             "beforerowselect" : true,
35421         /**
35422              * @event rowselect
35423              * Fires when a row is selected.
35424              * @param {SelectionModel} this
35425              * @param {Number} rowIndex The selected index
35426              * @param {Roo.data.Record} r The record
35427              */
35428             "rowselect" : true,
35429         /**
35430              * @event rowdeselect
35431              * Fires when a row is deselected.
35432              * @param {SelectionModel} this
35433              * @param {Number} rowIndex The selected index
35434              */
35435         "rowdeselect" : true
35436     });
35437     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35438     this.locked = false;
35439 };
35440
35441 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35442     /**
35443      * @cfg {Boolean} singleSelect
35444      * True to allow selection of only one row at a time (defaults to false)
35445      */
35446     singleSelect : false,
35447
35448     // private
35449     initEvents : function(){
35450
35451         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35452             this.grid.on("mousedown", this.handleMouseDown, this);
35453         }else{ // allow click to work like normal
35454             this.grid.on("rowclick", this.handleDragableRowClick, this);
35455         }
35456
35457         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35458             "up" : function(e){
35459                 if(!e.shiftKey){
35460                     this.selectPrevious(e.shiftKey);
35461                 }else if(this.last !== false && this.lastActive !== false){
35462                     var last = this.last;
35463                     this.selectRange(this.last,  this.lastActive-1);
35464                     this.grid.getView().focusRow(this.lastActive);
35465                     if(last !== false){
35466                         this.last = last;
35467                     }
35468                 }else{
35469                     this.selectFirstRow();
35470                 }
35471                 this.fireEvent("afterselectionchange", this);
35472             },
35473             "down" : function(e){
35474                 if(!e.shiftKey){
35475                     this.selectNext(e.shiftKey);
35476                 }else if(this.last !== false && this.lastActive !== false){
35477                     var last = this.last;
35478                     this.selectRange(this.last,  this.lastActive+1);
35479                     this.grid.getView().focusRow(this.lastActive);
35480                     if(last !== false){
35481                         this.last = last;
35482                     }
35483                 }else{
35484                     this.selectFirstRow();
35485                 }
35486                 this.fireEvent("afterselectionchange", this);
35487             },
35488             scope: this
35489         });
35490
35491         var view = this.grid.view;
35492         view.on("refresh", this.onRefresh, this);
35493         view.on("rowupdated", this.onRowUpdated, this);
35494         view.on("rowremoved", this.onRemove, this);
35495     },
35496
35497     // private
35498     onRefresh : function(){
35499         var ds = this.grid.dataSource, i, v = this.grid.view;
35500         var s = this.selections;
35501         s.each(function(r){
35502             if((i = ds.indexOfId(r.id)) != -1){
35503                 v.onRowSelect(i);
35504             }else{
35505                 s.remove(r);
35506             }
35507         });
35508     },
35509
35510     // private
35511     onRemove : function(v, index, r){
35512         this.selections.remove(r);
35513     },
35514
35515     // private
35516     onRowUpdated : function(v, index, r){
35517         if(this.isSelected(r)){
35518             v.onRowSelect(index);
35519         }
35520     },
35521
35522     /**
35523      * Select records.
35524      * @param {Array} records The records to select
35525      * @param {Boolean} keepExisting (optional) True to keep existing selections
35526      */
35527     selectRecords : function(records, keepExisting){
35528         if(!keepExisting){
35529             this.clearSelections();
35530         }
35531         var ds = this.grid.dataSource;
35532         for(var i = 0, len = records.length; i < len; i++){
35533             this.selectRow(ds.indexOf(records[i]), true);
35534         }
35535     },
35536
35537     /**
35538      * Gets the number of selected rows.
35539      * @return {Number}
35540      */
35541     getCount : function(){
35542         return this.selections.length;
35543     },
35544
35545     /**
35546      * Selects the first row in the grid.
35547      */
35548     selectFirstRow : function(){
35549         this.selectRow(0);
35550     },
35551
35552     /**
35553      * Select the last row.
35554      * @param {Boolean} keepExisting (optional) True to keep existing selections
35555      */
35556     selectLastRow : function(keepExisting){
35557         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35558     },
35559
35560     /**
35561      * Selects the row immediately following the last selected row.
35562      * @param {Boolean} keepExisting (optional) True to keep existing selections
35563      */
35564     selectNext : function(keepExisting){
35565         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35566             this.selectRow(this.last+1, keepExisting);
35567             this.grid.getView().focusRow(this.last);
35568         }
35569     },
35570
35571     /**
35572      * Selects the row that precedes the last selected row.
35573      * @param {Boolean} keepExisting (optional) True to keep existing selections
35574      */
35575     selectPrevious : function(keepExisting){
35576         if(this.last){
35577             this.selectRow(this.last-1, keepExisting);
35578             this.grid.getView().focusRow(this.last);
35579         }
35580     },
35581
35582     /**
35583      * Returns the selected records
35584      * @return {Array} Array of selected records
35585      */
35586     getSelections : function(){
35587         return [].concat(this.selections.items);
35588     },
35589
35590     /**
35591      * Returns the first selected record.
35592      * @return {Record}
35593      */
35594     getSelected : function(){
35595         return this.selections.itemAt(0);
35596     },
35597
35598
35599     /**
35600      * Clears all selections.
35601      */
35602     clearSelections : function(fast){
35603         if(this.locked) return;
35604         if(fast !== true){
35605             var ds = this.grid.dataSource;
35606             var s = this.selections;
35607             s.each(function(r){
35608                 this.deselectRow(ds.indexOfId(r.id));
35609             }, this);
35610             s.clear();
35611         }else{
35612             this.selections.clear();
35613         }
35614         this.last = false;
35615     },
35616
35617
35618     /**
35619      * Selects all rows.
35620      */
35621     selectAll : function(){
35622         if(this.locked) return;
35623         this.selections.clear();
35624         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35625             this.selectRow(i, true);
35626         }
35627     },
35628
35629     /**
35630      * Returns True if there is a selection.
35631      * @return {Boolean}
35632      */
35633     hasSelection : function(){
35634         return this.selections.length > 0;
35635     },
35636
35637     /**
35638      * Returns True if the specified row is selected.
35639      * @param {Number/Record} record The record or index of the record to check
35640      * @return {Boolean}
35641      */
35642     isSelected : function(index){
35643         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35644         return (r && this.selections.key(r.id) ? true : false);
35645     },
35646
35647     /**
35648      * Returns True if the specified record id is selected.
35649      * @param {String} id The id of record to check
35650      * @return {Boolean}
35651      */
35652     isIdSelected : function(id){
35653         return (this.selections.key(id) ? true : false);
35654     },
35655
35656     // private
35657     handleMouseDown : function(e, t){
35658         var view = this.grid.getView(), rowIndex;
35659         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35660             return;
35661         };
35662         if(e.shiftKey && this.last !== false){
35663             var last = this.last;
35664             this.selectRange(last, rowIndex, e.ctrlKey);
35665             this.last = last; // reset the last
35666             view.focusRow(rowIndex);
35667         }else{
35668             var isSelected = this.isSelected(rowIndex);
35669             if(e.button !== 0 && isSelected){
35670                 view.focusRow(rowIndex);
35671             }else if(e.ctrlKey && isSelected){
35672                 this.deselectRow(rowIndex);
35673             }else if(!isSelected){
35674                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35675                 view.focusRow(rowIndex);
35676             }
35677         }
35678         this.fireEvent("afterselectionchange", this);
35679     },
35680     // private
35681     handleDragableRowClick :  function(grid, rowIndex, e) 
35682     {
35683         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35684             this.selectRow(rowIndex, false);
35685             grid.view.focusRow(rowIndex);
35686              this.fireEvent("afterselectionchange", this);
35687         }
35688     },
35689     
35690     /**
35691      * Selects multiple rows.
35692      * @param {Array} rows Array of the indexes of the row to select
35693      * @param {Boolean} keepExisting (optional) True to keep existing selections
35694      */
35695     selectRows : function(rows, keepExisting){
35696         if(!keepExisting){
35697             this.clearSelections();
35698         }
35699         for(var i = 0, len = rows.length; i < len; i++){
35700             this.selectRow(rows[i], true);
35701         }
35702     },
35703
35704     /**
35705      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35706      * @param {Number} startRow The index of the first row in the range
35707      * @param {Number} endRow The index of the last row in the range
35708      * @param {Boolean} keepExisting (optional) True to retain existing selections
35709      */
35710     selectRange : function(startRow, endRow, keepExisting){
35711         if(this.locked) return;
35712         if(!keepExisting){
35713             this.clearSelections();
35714         }
35715         if(startRow <= endRow){
35716             for(var i = startRow; i <= endRow; i++){
35717                 this.selectRow(i, true);
35718             }
35719         }else{
35720             for(var i = startRow; i >= endRow; i--){
35721                 this.selectRow(i, true);
35722             }
35723         }
35724     },
35725
35726     /**
35727      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35728      * @param {Number} startRow The index of the first row in the range
35729      * @param {Number} endRow The index of the last row in the range
35730      */
35731     deselectRange : function(startRow, endRow, preventViewNotify){
35732         if(this.locked) return;
35733         for(var i = startRow; i <= endRow; i++){
35734             this.deselectRow(i, preventViewNotify);
35735         }
35736     },
35737
35738     /**
35739      * Selects a row.
35740      * @param {Number} row The index of the row to select
35741      * @param {Boolean} keepExisting (optional) True to keep existing selections
35742      */
35743     selectRow : function(index, keepExisting, preventViewNotify){
35744         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35745         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35746             if(!keepExisting || this.singleSelect){
35747                 this.clearSelections();
35748             }
35749             var r = this.grid.dataSource.getAt(index);
35750             this.selections.add(r);
35751             this.last = this.lastActive = index;
35752             if(!preventViewNotify){
35753                 this.grid.getView().onRowSelect(index);
35754             }
35755             this.fireEvent("rowselect", this, index, r);
35756             this.fireEvent("selectionchange", this);
35757         }
35758     },
35759
35760     /**
35761      * Deselects a row.
35762      * @param {Number} row The index of the row to deselect
35763      */
35764     deselectRow : function(index, preventViewNotify){
35765         if(this.locked) return;
35766         if(this.last == index){
35767             this.last = false;
35768         }
35769         if(this.lastActive == index){
35770             this.lastActive = false;
35771         }
35772         var r = this.grid.dataSource.getAt(index);
35773         this.selections.remove(r);
35774         if(!preventViewNotify){
35775             this.grid.getView().onRowDeselect(index);
35776         }
35777         this.fireEvent("rowdeselect", this, index);
35778         this.fireEvent("selectionchange", this);
35779     },
35780
35781     // private
35782     restoreLast : function(){
35783         if(this._last){
35784             this.last = this._last;
35785         }
35786     },
35787
35788     // private
35789     acceptsNav : function(row, col, cm){
35790         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35791     },
35792
35793     // private
35794     onEditorKey : function(field, e){
35795         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35796         if(k == e.TAB){
35797             e.stopEvent();
35798             ed.completeEdit();
35799             if(e.shiftKey){
35800                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35801             }else{
35802                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35803             }
35804         }else if(k == e.ENTER && !e.ctrlKey){
35805             e.stopEvent();
35806             ed.completeEdit();
35807             if(e.shiftKey){
35808                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35809             }else{
35810                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35811             }
35812         }else if(k == e.ESC){
35813             ed.cancelEdit();
35814         }
35815         if(newCell){
35816             g.startEditing(newCell[0], newCell[1]);
35817         }
35818     }
35819 });/*
35820  * Based on:
35821  * Ext JS Library 1.1.1
35822  * Copyright(c) 2006-2007, Ext JS, LLC.
35823  *
35824  * Originally Released Under LGPL - original licence link has changed is not relivant.
35825  *
35826  * Fork - LGPL
35827  * <script type="text/javascript">
35828  */
35829 /**
35830  * @class Roo.grid.CellSelectionModel
35831  * @extends Roo.grid.AbstractSelectionModel
35832  * This class provides the basic implementation for cell selection in a grid.
35833  * @constructor
35834  * @param {Object} config The object containing the configuration of this model.
35835  */
35836 Roo.grid.CellSelectionModel = function(config){
35837     Roo.apply(this, config);
35838
35839     this.selection = null;
35840
35841     this.addEvents({
35842         /**
35843              * @event beforerowselect
35844              * Fires before a cell is selected.
35845              * @param {SelectionModel} this
35846              * @param {Number} rowIndex The selected row index
35847              * @param {Number} colIndex The selected cell index
35848              */
35849             "beforecellselect" : true,
35850         /**
35851              * @event cellselect
35852              * Fires when a cell is selected.
35853              * @param {SelectionModel} this
35854              * @param {Number} rowIndex The selected row index
35855              * @param {Number} colIndex The selected cell index
35856              */
35857             "cellselect" : true,
35858         /**
35859              * @event selectionchange
35860              * Fires when the active selection changes.
35861              * @param {SelectionModel} this
35862              * @param {Object} selection null for no selection or an object (o) with two properties
35863                 <ul>
35864                 <li>o.record: the record object for the row the selection is in</li>
35865                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35866                 </ul>
35867              */
35868             "selectionchange" : true
35869     });
35870     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35871 };
35872
35873 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35874
35875     /** @ignore */
35876     initEvents : function(){
35877         this.grid.on("mousedown", this.handleMouseDown, this);
35878         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35879         var view = this.grid.view;
35880         view.on("refresh", this.onViewChange, this);
35881         view.on("rowupdated", this.onRowUpdated, this);
35882         view.on("beforerowremoved", this.clearSelections, this);
35883         view.on("beforerowsinserted", this.clearSelections, this);
35884         if(this.grid.isEditor){
35885             this.grid.on("beforeedit", this.beforeEdit,  this);
35886         }
35887     },
35888
35889         //private
35890     beforeEdit : function(e){
35891         this.select(e.row, e.column, false, true, e.record);
35892     },
35893
35894         //private
35895     onRowUpdated : function(v, index, r){
35896         if(this.selection && this.selection.record == r){
35897             v.onCellSelect(index, this.selection.cell[1]);
35898         }
35899     },
35900
35901         //private
35902     onViewChange : function(){
35903         this.clearSelections(true);
35904     },
35905
35906         /**
35907          * Returns the currently selected cell,.
35908          * @return {Array} The selected cell (row, column) or null if none selected.
35909          */
35910     getSelectedCell : function(){
35911         return this.selection ? this.selection.cell : null;
35912     },
35913
35914     /**
35915      * Clears all selections.
35916      * @param {Boolean} true to prevent the gridview from being notified about the change.
35917      */
35918     clearSelections : function(preventNotify){
35919         var s = this.selection;
35920         if(s){
35921             if(preventNotify !== true){
35922                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35923             }
35924             this.selection = null;
35925             this.fireEvent("selectionchange", this, null);
35926         }
35927     },
35928
35929     /**
35930      * Returns true if there is a selection.
35931      * @return {Boolean}
35932      */
35933     hasSelection : function(){
35934         return this.selection ? true : false;
35935     },
35936
35937     /** @ignore */
35938     handleMouseDown : function(e, t){
35939         var v = this.grid.getView();
35940         if(this.isLocked()){
35941             return;
35942         };
35943         var row = v.findRowIndex(t);
35944         var cell = v.findCellIndex(t);
35945         if(row !== false && cell !== false){
35946             this.select(row, cell);
35947         }
35948     },
35949
35950     /**
35951      * Selects a cell.
35952      * @param {Number} rowIndex
35953      * @param {Number} collIndex
35954      */
35955     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35956         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35957             this.clearSelections();
35958             r = r || this.grid.dataSource.getAt(rowIndex);
35959             this.selection = {
35960                 record : r,
35961                 cell : [rowIndex, colIndex]
35962             };
35963             if(!preventViewNotify){
35964                 var v = this.grid.getView();
35965                 v.onCellSelect(rowIndex, colIndex);
35966                 if(preventFocus !== true){
35967                     v.focusCell(rowIndex, colIndex);
35968                 }
35969             }
35970             this.fireEvent("cellselect", this, rowIndex, colIndex);
35971             this.fireEvent("selectionchange", this, this.selection);
35972         }
35973     },
35974
35975         //private
35976     isSelectable : function(rowIndex, colIndex, cm){
35977         return !cm.isHidden(colIndex);
35978     },
35979
35980     /** @ignore */
35981     handleKeyDown : function(e){
35982         Roo.log('Cell Sel Model handleKeyDown');
35983         if(!e.isNavKeyPress()){
35984             return;
35985         }
35986         var g = this.grid, s = this.selection;
35987         if(!s){
35988             e.stopEvent();
35989             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35990             if(cell){
35991                 this.select(cell[0], cell[1]);
35992             }
35993             return;
35994         }
35995         var sm = this;
35996         var walk = function(row, col, step){
35997             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35998         };
35999         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36000         var newCell;
36001
36002         switch(k){
36003             case e.TAB:
36004                 // handled by onEditorKey
36005                 if (g.isEditor && g.editing) {
36006                     return;
36007                 }
36008                 if(e.shiftKey){
36009                      newCell = walk(r, c-1, -1);
36010                 }else{
36011                      newCell = walk(r, c+1, 1);
36012                 }
36013              break;
36014              case e.DOWN:
36015                  newCell = walk(r+1, c, 1);
36016              break;
36017              case e.UP:
36018                  newCell = walk(r-1, c, -1);
36019              break;
36020              case e.RIGHT:
36021                  newCell = walk(r, c+1, 1);
36022              break;
36023              case e.LEFT:
36024                  newCell = walk(r, c-1, -1);
36025              break;
36026              case e.ENTER:
36027                  if(g.isEditor && !g.editing){
36028                     g.startEditing(r, c);
36029                     e.stopEvent();
36030                     return;
36031                 }
36032              break;
36033         };
36034         if(newCell){
36035             this.select(newCell[0], newCell[1]);
36036             e.stopEvent();
36037         }
36038     },
36039
36040     acceptsNav : function(row, col, cm){
36041         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36042     },
36043
36044     onEditorKey : function(field, e){
36045         
36046         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36047         ///Roo.log('onEditorKey' + k);
36048         
36049         if(k == e.TAB){
36050             if(e.shiftKey){
36051                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36052             }else{
36053                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36054             }
36055             e.stopEvent();
36056         }else if(k == e.ENTER && !e.ctrlKey){
36057             ed.completeEdit();
36058             e.stopEvent();
36059             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36060         }else if(k == e.ESC){
36061             ed.cancelEdit();
36062         }
36063         
36064         
36065         if(newCell){
36066             //Roo.log('next cell after edit');
36067             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36068         }
36069     }
36070 });/*
36071  * Based on:
36072  * Ext JS Library 1.1.1
36073  * Copyright(c) 2006-2007, Ext JS, LLC.
36074  *
36075  * Originally Released Under LGPL - original licence link has changed is not relivant.
36076  *
36077  * Fork - LGPL
36078  * <script type="text/javascript">
36079  */
36080  
36081 /**
36082  * @class Roo.grid.EditorGrid
36083  * @extends Roo.grid.Grid
36084  * Class for creating and editable grid.
36085  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36086  * The container MUST have some type of size defined for the grid to fill. The container will be 
36087  * automatically set to position relative if it isn't already.
36088  * @param {Object} dataSource The data model to bind to
36089  * @param {Object} colModel The column model with info about this grid's columns
36090  */
36091 Roo.grid.EditorGrid = function(container, config){
36092     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36093     this.getGridEl().addClass("xedit-grid");
36094
36095     if(!this.selModel){
36096         this.selModel = new Roo.grid.CellSelectionModel();
36097     }
36098
36099     this.activeEditor = null;
36100
36101         this.addEvents({
36102             /**
36103              * @event beforeedit
36104              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36105              * <ul style="padding:5px;padding-left:16px;">
36106              * <li>grid - This grid</li>
36107              * <li>record - The record being edited</li>
36108              * <li>field - The field name being edited</li>
36109              * <li>value - The value for the field being edited.</li>
36110              * <li>row - The grid row index</li>
36111              * <li>column - The grid column index</li>
36112              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36113              * </ul>
36114              * @param {Object} e An edit event (see above for description)
36115              */
36116             "beforeedit" : true,
36117             /**
36118              * @event afteredit
36119              * Fires after a cell is edited. <br />
36120              * <ul style="padding:5px;padding-left:16px;">
36121              * <li>grid - This grid</li>
36122              * <li>record - The record being edited</li>
36123              * <li>field - The field name being edited</li>
36124              * <li>value - The value being set</li>
36125              * <li>originalValue - The original value for the field, before the edit.</li>
36126              * <li>row - The grid row index</li>
36127              * <li>column - The grid column index</li>
36128              * </ul>
36129              * @param {Object} e An edit event (see above for description)
36130              */
36131             "afteredit" : true,
36132             /**
36133              * @event validateedit
36134              * Fires after a cell is edited, but before the value is set in the record. 
36135          * You can use this to modify the value being set in the field, Return false
36136              * to cancel the change. The edit event object has the following properties <br />
36137              * <ul style="padding:5px;padding-left:16px;">
36138          * <li>editor - This editor</li>
36139              * <li>grid - This grid</li>
36140              * <li>record - The record being edited</li>
36141              * <li>field - The field name being edited</li>
36142              * <li>value - The value being set</li>
36143              * <li>originalValue - The original value for the field, before the edit.</li>
36144              * <li>row - The grid row index</li>
36145              * <li>column - The grid column index</li>
36146              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36147              * </ul>
36148              * @param {Object} e An edit event (see above for description)
36149              */
36150             "validateedit" : true
36151         });
36152     this.on("bodyscroll", this.stopEditing,  this);
36153     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36154 };
36155
36156 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36157     /**
36158      * @cfg {Number} clicksToEdit
36159      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36160      */
36161     clicksToEdit: 2,
36162
36163     // private
36164     isEditor : true,
36165     // private
36166     trackMouseOver: false, // causes very odd FF errors
36167
36168     onCellDblClick : function(g, row, col){
36169         this.startEditing(row, col);
36170     },
36171
36172     onEditComplete : function(ed, value, startValue){
36173         this.editing = false;
36174         this.activeEditor = null;
36175         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36176         var r = ed.record;
36177         var field = this.colModel.getDataIndex(ed.col);
36178         var e = {
36179             grid: this,
36180             record: r,
36181             field: field,
36182             originalValue: startValue,
36183             value: value,
36184             row: ed.row,
36185             column: ed.col,
36186             cancel:false,
36187             editor: ed
36188         };
36189         if(String(value) !== String(startValue)){
36190             
36191             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36192                 r.set(field, e.value);
36193                 // if we are dealing with a combo box..
36194                 // then we also set the 'name' colum to be the displayField
36195                 if (ed.field.displayField && ed.field.name) {
36196                     r.set(ed.field.name, ed.field.el.dom.value);
36197                 }
36198                 
36199                 delete e.cancel; //?? why!!!
36200                 this.fireEvent("afteredit", e);
36201             }
36202         } else {
36203             this.fireEvent("afteredit", e); // always fire it!
36204         }
36205         this.view.focusCell(ed.row, ed.col);
36206     },
36207
36208     /**
36209      * Starts editing the specified for the specified row/column
36210      * @param {Number} rowIndex
36211      * @param {Number} colIndex
36212      */
36213     startEditing : function(row, col){
36214         this.stopEditing();
36215         if(this.colModel.isCellEditable(col, row)){
36216             this.view.ensureVisible(row, col, true);
36217             var r = this.dataSource.getAt(row);
36218             var field = this.colModel.getDataIndex(col);
36219             var e = {
36220                 grid: this,
36221                 record: r,
36222                 field: field,
36223                 value: r.data[field],
36224                 row: row,
36225                 column: col,
36226                 cancel:false
36227             };
36228             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36229                 this.editing = true;
36230                 var ed = this.colModel.getCellEditor(col, row);
36231                 
36232                 if (!ed) {
36233                     return;
36234                 }
36235                 if(!ed.rendered){
36236                     ed.render(ed.parentEl || document.body);
36237                 }
36238                 ed.field.reset();
36239                 (function(){ // complex but required for focus issues in safari, ie and opera
36240                     ed.row = row;
36241                     ed.col = col;
36242                     ed.record = r;
36243                     ed.on("complete", this.onEditComplete, this, {single: true});
36244                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36245                     this.activeEditor = ed;
36246                     var v = r.data[field];
36247                     ed.startEdit(this.view.getCell(row, col), v);
36248                     // combo's with 'displayField and name set
36249                     if (ed.field.displayField && ed.field.name) {
36250                         ed.field.el.dom.value = r.data[ed.field.name];
36251                     }
36252                     
36253                     
36254                 }).defer(50, this);
36255             }
36256         }
36257     },
36258         
36259     /**
36260      * Stops any active editing
36261      */
36262     stopEditing : function(){
36263         if(this.activeEditor){
36264             this.activeEditor.completeEdit();
36265         }
36266         this.activeEditor = null;
36267     }
36268 });/*
36269  * Based on:
36270  * Ext JS Library 1.1.1
36271  * Copyright(c) 2006-2007, Ext JS, LLC.
36272  *
36273  * Originally Released Under LGPL - original licence link has changed is not relivant.
36274  *
36275  * Fork - LGPL
36276  * <script type="text/javascript">
36277  */
36278
36279 // private - not really -- you end up using it !
36280 // This is a support class used internally by the Grid components
36281
36282 /**
36283  * @class Roo.grid.GridEditor
36284  * @extends Roo.Editor
36285  * Class for creating and editable grid elements.
36286  * @param {Object} config any settings (must include field)
36287  */
36288 Roo.grid.GridEditor = function(field, config){
36289     if (!config && field.field) {
36290         config = field;
36291         field = Roo.factory(config.field, Roo.form);
36292     }
36293     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36294     field.monitorTab = false;
36295 };
36296
36297 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36298     
36299     /**
36300      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36301      */
36302     
36303     alignment: "tl-tl",
36304     autoSize: "width",
36305     hideEl : false,
36306     cls: "x-small-editor x-grid-editor",
36307     shim:false,
36308     shadow:"frame"
36309 });/*
36310  * Based on:
36311  * Ext JS Library 1.1.1
36312  * Copyright(c) 2006-2007, Ext JS, LLC.
36313  *
36314  * Originally Released Under LGPL - original licence link has changed is not relivant.
36315  *
36316  * Fork - LGPL
36317  * <script type="text/javascript">
36318  */
36319   
36320
36321   
36322 Roo.grid.PropertyRecord = Roo.data.Record.create([
36323     {name:'name',type:'string'},  'value'
36324 ]);
36325
36326
36327 Roo.grid.PropertyStore = function(grid, source){
36328     this.grid = grid;
36329     this.store = new Roo.data.Store({
36330         recordType : Roo.grid.PropertyRecord
36331     });
36332     this.store.on('update', this.onUpdate,  this);
36333     if(source){
36334         this.setSource(source);
36335     }
36336     Roo.grid.PropertyStore.superclass.constructor.call(this);
36337 };
36338
36339
36340
36341 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36342     setSource : function(o){
36343         this.source = o;
36344         this.store.removeAll();
36345         var data = [];
36346         for(var k in o){
36347             if(this.isEditableValue(o[k])){
36348                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36349             }
36350         }
36351         this.store.loadRecords({records: data}, {}, true);
36352     },
36353
36354     onUpdate : function(ds, record, type){
36355         if(type == Roo.data.Record.EDIT){
36356             var v = record.data['value'];
36357             var oldValue = record.modified['value'];
36358             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36359                 this.source[record.id] = v;
36360                 record.commit();
36361                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36362             }else{
36363                 record.reject();
36364             }
36365         }
36366     },
36367
36368     getProperty : function(row){
36369        return this.store.getAt(row);
36370     },
36371
36372     isEditableValue: function(val){
36373         if(val && val instanceof Date){
36374             return true;
36375         }else if(typeof val == 'object' || typeof val == 'function'){
36376             return false;
36377         }
36378         return true;
36379     },
36380
36381     setValue : function(prop, value){
36382         this.source[prop] = value;
36383         this.store.getById(prop).set('value', value);
36384     },
36385
36386     getSource : function(){
36387         return this.source;
36388     }
36389 });
36390
36391 Roo.grid.PropertyColumnModel = function(grid, store){
36392     this.grid = grid;
36393     var g = Roo.grid;
36394     g.PropertyColumnModel.superclass.constructor.call(this, [
36395         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36396         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36397     ]);
36398     this.store = store;
36399     this.bselect = Roo.DomHelper.append(document.body, {
36400         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36401             {tag: 'option', value: 'true', html: 'true'},
36402             {tag: 'option', value: 'false', html: 'false'}
36403         ]
36404     });
36405     Roo.id(this.bselect);
36406     var f = Roo.form;
36407     this.editors = {
36408         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36409         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36410         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36411         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36412         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36413     };
36414     this.renderCellDelegate = this.renderCell.createDelegate(this);
36415     this.renderPropDelegate = this.renderProp.createDelegate(this);
36416 };
36417
36418 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36419     
36420     
36421     nameText : 'Name',
36422     valueText : 'Value',
36423     
36424     dateFormat : 'm/j/Y',
36425     
36426     
36427     renderDate : function(dateVal){
36428         return dateVal.dateFormat(this.dateFormat);
36429     },
36430
36431     renderBool : function(bVal){
36432         return bVal ? 'true' : 'false';
36433     },
36434
36435     isCellEditable : function(colIndex, rowIndex){
36436         return colIndex == 1;
36437     },
36438
36439     getRenderer : function(col){
36440         return col == 1 ?
36441             this.renderCellDelegate : this.renderPropDelegate;
36442     },
36443
36444     renderProp : function(v){
36445         return this.getPropertyName(v);
36446     },
36447
36448     renderCell : function(val){
36449         var rv = val;
36450         if(val instanceof Date){
36451             rv = this.renderDate(val);
36452         }else if(typeof val == 'boolean'){
36453             rv = this.renderBool(val);
36454         }
36455         return Roo.util.Format.htmlEncode(rv);
36456     },
36457
36458     getPropertyName : function(name){
36459         var pn = this.grid.propertyNames;
36460         return pn && pn[name] ? pn[name] : name;
36461     },
36462
36463     getCellEditor : function(colIndex, rowIndex){
36464         var p = this.store.getProperty(rowIndex);
36465         var n = p.data['name'], val = p.data['value'];
36466         
36467         if(typeof(this.grid.customEditors[n]) == 'string'){
36468             return this.editors[this.grid.customEditors[n]];
36469         }
36470         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36471             return this.grid.customEditors[n];
36472         }
36473         if(val instanceof Date){
36474             return this.editors['date'];
36475         }else if(typeof val == 'number'){
36476             return this.editors['number'];
36477         }else if(typeof val == 'boolean'){
36478             return this.editors['boolean'];
36479         }else{
36480             return this.editors['string'];
36481         }
36482     }
36483 });
36484
36485 /**
36486  * @class Roo.grid.PropertyGrid
36487  * @extends Roo.grid.EditorGrid
36488  * This class represents the  interface of a component based property grid control.
36489  * <br><br>Usage:<pre><code>
36490  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36491       
36492  });
36493  // set any options
36494  grid.render();
36495  * </code></pre>
36496   
36497  * @constructor
36498  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36499  * The container MUST have some type of size defined for the grid to fill. The container will be
36500  * automatically set to position relative if it isn't already.
36501  * @param {Object} config A config object that sets properties on this grid.
36502  */
36503 Roo.grid.PropertyGrid = function(container, config){
36504     config = config || {};
36505     var store = new Roo.grid.PropertyStore(this);
36506     this.store = store;
36507     var cm = new Roo.grid.PropertyColumnModel(this, store);
36508     store.store.sort('name', 'ASC');
36509     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36510         ds: store.store,
36511         cm: cm,
36512         enableColLock:false,
36513         enableColumnMove:false,
36514         stripeRows:false,
36515         trackMouseOver: false,
36516         clicksToEdit:1
36517     }, config));
36518     this.getGridEl().addClass('x-props-grid');
36519     this.lastEditRow = null;
36520     this.on('columnresize', this.onColumnResize, this);
36521     this.addEvents({
36522          /**
36523              * @event beforepropertychange
36524              * Fires before a property changes (return false to stop?)
36525              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36526              * @param {String} id Record Id
36527              * @param {String} newval New Value
36528          * @param {String} oldval Old Value
36529              */
36530         "beforepropertychange": true,
36531         /**
36532              * @event propertychange
36533              * Fires after a property changes
36534              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36535              * @param {String} id Record Id
36536              * @param {String} newval New Value
36537          * @param {String} oldval Old Value
36538              */
36539         "propertychange": true
36540     });
36541     this.customEditors = this.customEditors || {};
36542 };
36543 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36544     
36545      /**
36546      * @cfg {Object} customEditors map of colnames=> custom editors.
36547      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36548      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36549      * false disables editing of the field.
36550          */
36551     
36552       /**
36553      * @cfg {Object} propertyNames map of property Names to their displayed value
36554          */
36555     
36556     render : function(){
36557         Roo.grid.PropertyGrid.superclass.render.call(this);
36558         this.autoSize.defer(100, this);
36559     },
36560
36561     autoSize : function(){
36562         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36563         if(this.view){
36564             this.view.fitColumns();
36565         }
36566     },
36567
36568     onColumnResize : function(){
36569         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36570         this.autoSize();
36571     },
36572     /**
36573      * Sets the data for the Grid
36574      * accepts a Key => Value object of all the elements avaiable.
36575      * @param {Object} data  to appear in grid.
36576      */
36577     setSource : function(source){
36578         this.store.setSource(source);
36579         //this.autoSize();
36580     },
36581     /**
36582      * Gets all the data from the grid.
36583      * @return {Object} data  data stored in grid
36584      */
36585     getSource : function(){
36586         return this.store.getSource();
36587     }
36588 });/*
36589  * Based on:
36590  * Ext JS Library 1.1.1
36591  * Copyright(c) 2006-2007, Ext JS, LLC.
36592  *
36593  * Originally Released Under LGPL - original licence link has changed is not relivant.
36594  *
36595  * Fork - LGPL
36596  * <script type="text/javascript">
36597  */
36598  
36599 /**
36600  * @class Roo.LoadMask
36601  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36602  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36603  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36604  * element's UpdateManager load indicator and will be destroyed after the initial load.
36605  * @constructor
36606  * Create a new LoadMask
36607  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36608  * @param {Object} config The config object
36609  */
36610 Roo.LoadMask = function(el, config){
36611     this.el = Roo.get(el);
36612     Roo.apply(this, config);
36613     if(this.store){
36614         this.store.on('beforeload', this.onBeforeLoad, this);
36615         this.store.on('load', this.onLoad, this);
36616         this.store.on('loadexception', this.onLoad, this);
36617         this.removeMask = false;
36618     }else{
36619         var um = this.el.getUpdateManager();
36620         um.showLoadIndicator = false; // disable the default indicator
36621         um.on('beforeupdate', this.onBeforeLoad, this);
36622         um.on('update', this.onLoad, this);
36623         um.on('failure', this.onLoad, this);
36624         this.removeMask = true;
36625     }
36626 };
36627
36628 Roo.LoadMask.prototype = {
36629     /**
36630      * @cfg {Boolean} removeMask
36631      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36632      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36633      */
36634     /**
36635      * @cfg {String} msg
36636      * The text to display in a centered loading message box (defaults to 'Loading...')
36637      */
36638     msg : 'Loading...',
36639     /**
36640      * @cfg {String} msgCls
36641      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36642      */
36643     msgCls : 'x-mask-loading',
36644
36645     /**
36646      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36647      * @type Boolean
36648      */
36649     disabled: false,
36650
36651     /**
36652      * Disables the mask to prevent it from being displayed
36653      */
36654     disable : function(){
36655        this.disabled = true;
36656     },
36657
36658     /**
36659      * Enables the mask so that it can be displayed
36660      */
36661     enable : function(){
36662         this.disabled = false;
36663     },
36664
36665     // private
36666     onLoad : function(){
36667         this.el.unmask(this.removeMask);
36668     },
36669
36670     // private
36671     onBeforeLoad : function(){
36672         if(!this.disabled){
36673             this.el.mask(this.msg, this.msgCls);
36674         }
36675     },
36676
36677     // private
36678     destroy : function(){
36679         if(this.store){
36680             this.store.un('beforeload', this.onBeforeLoad, this);
36681             this.store.un('load', this.onLoad, this);
36682             this.store.un('loadexception', this.onLoad, this);
36683         }else{
36684             var um = this.el.getUpdateManager();
36685             um.un('beforeupdate', this.onBeforeLoad, this);
36686             um.un('update', this.onLoad, this);
36687             um.un('failure', this.onLoad, this);
36688         }
36689     }
36690 };/*
36691  * Based on:
36692  * Ext JS Library 1.1.1
36693  * Copyright(c) 2006-2007, Ext JS, LLC.
36694  *
36695  * Originally Released Under LGPL - original licence link has changed is not relivant.
36696  *
36697  * Fork - LGPL
36698  * <script type="text/javascript">
36699  */
36700 Roo.XTemplate = function(){
36701     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36702     var s = this.html;
36703
36704     s = ['<tpl>', s, '</tpl>'].join('');
36705
36706     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36707
36708     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36709     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36710     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36711     var m, id = 0;
36712     var tpls = [];
36713
36714     while(m = s.match(re)){
36715        var m2 = m[0].match(nameRe);
36716        var m3 = m[0].match(ifRe);
36717        var m4 = m[0].match(execRe);
36718        var exp = null, fn = null, exec = null;
36719        var name = m2 && m2[1] ? m2[1] : '';
36720        if(m3){
36721            exp = m3 && m3[1] ? m3[1] : null;
36722            if(exp){
36723                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36724            }
36725        }
36726        if(m4){
36727            exp = m4 && m4[1] ? m4[1] : null;
36728            if(exp){
36729                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36730            }
36731        }
36732        if(name){
36733            switch(name){
36734                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36735                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36736                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36737            }
36738        }
36739        tpls.push({
36740             id: id,
36741             target: name,
36742             exec: exec,
36743             test: fn,
36744             body: m[1]||''
36745         });
36746        s = s.replace(m[0], '{xtpl'+ id + '}');
36747        ++id;
36748     }
36749     for(var i = tpls.length-1; i >= 0; --i){
36750         this.compileTpl(tpls[i]);
36751     }
36752     this.master = tpls[tpls.length-1];
36753     this.tpls = tpls;
36754 };
36755 Roo.extend(Roo.XTemplate, Roo.Template, {
36756
36757     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36758
36759     applySubTemplate : function(id, values, parent){
36760         var t = this.tpls[id];
36761         if(t.test && !t.test.call(this, values, parent)){
36762             return '';
36763         }
36764         if(t.exec && t.exec.call(this, values, parent)){
36765             return '';
36766         }
36767         var vs = t.target ? t.target.call(this, values, parent) : values;
36768         parent = t.target ? values : parent;
36769         if(t.target && vs instanceof Array){
36770             var buf = [];
36771             for(var i = 0, len = vs.length; i < len; i++){
36772                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36773             }
36774             return buf.join('');
36775         }
36776         return t.compiled.call(this, vs, parent);
36777     },
36778
36779     compileTpl : function(tpl){
36780         var fm = Roo.util.Format;
36781         var useF = this.disableFormats !== true;
36782         var sep = Roo.isGecko ? "+" : ",";
36783         var fn = function(m, name, format, args){
36784             if(name.substr(0, 4) == 'xtpl'){
36785                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36786             }
36787             var v;
36788             if(name.indexOf('.') != -1){
36789                 v = name;
36790             }else{
36791                 v = "values['" + name + "']";
36792             }
36793             if(format && useF){
36794                 args = args ? ',' + args : "";
36795                 if(format.substr(0, 5) != "this."){
36796                     format = "fm." + format + '(';
36797                 }else{
36798                     format = 'this.call("'+ format.substr(5) + '", ';
36799                     args = ", values";
36800                 }
36801             }else{
36802                 args= ''; format = "("+v+" === undefined ? '' : ";
36803             }
36804             return "'"+ sep + format + v + args + ")"+sep+"'";
36805         };
36806         var body;
36807         // branched to use + in gecko and [].join() in others
36808         if(Roo.isGecko){
36809             body = "tpl.compiled = function(values, parent){ return '" +
36810                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36811                     "';};";
36812         }else{
36813             body = ["tpl.compiled = function(values, parent){ return ['"];
36814             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36815             body.push("'].join('');};");
36816             body = body.join('');
36817         }
36818         /** eval:var:zzzzzzz */
36819         eval(body);
36820         return this;
36821     },
36822
36823     applyTemplate : function(values){
36824         return this.master.compiled.call(this, values, {});
36825         var s = this.subs;
36826     },
36827
36828     apply : function(){
36829         return this.applyTemplate.apply(this, arguments);
36830     },
36831
36832     compile : function(){return this;}
36833 });
36834
36835 Roo.XTemplate.from = function(el){
36836     el = Roo.getDom(el);
36837     return new Roo.XTemplate(el.value || el.innerHTML);
36838 };/*
36839  * Original code for Roojs - LGPL
36840  * <script type="text/javascript">
36841  */
36842  
36843 /**
36844  * @class Roo.XComponent
36845  * A delayed Element creator...
36846  * Or a way to group chunks of interface together.
36847  * 
36848  * Mypart.xyx = new Roo.XComponent({
36849
36850     parent : 'Mypart.xyz', // empty == document.element.!!
36851     order : '001',
36852     name : 'xxxx'
36853     region : 'xxxx'
36854     disabled : function() {} 
36855      
36856     tree : function() { // return an tree of xtype declared components
36857         var MODULE = this;
36858         return 
36859         {
36860             xtype : 'NestedLayoutPanel',
36861             // technicall
36862         }
36863      ]
36864  *})
36865  *
36866  *
36867  * It can be used to build a big heiracy, with parent etc.
36868  * or you can just use this to render a single compoent to a dom element
36869  * MYPART.render(Roo.Element | String(id) | dom_element )
36870  * 
36871  * @extends Roo.util.Observable
36872  * @constructor
36873  * @param cfg {Object} configuration of component
36874  * 
36875  */
36876 Roo.XComponent = function(cfg) {
36877     Roo.apply(this, cfg);
36878     this.addEvents({ 
36879         /**
36880              * @event built
36881              * Fires when this the componnt is built
36882              * @param {Roo.XComponent} c the component
36883              */
36884         'built' : true,
36885         /**
36886              * @event buildcomplete
36887              * Fires on the top level element when all elements have been built
36888              * @param {Roo.XComponent} c the top level component.
36889          */
36890         'buildcomplete' : true
36891         
36892     });
36893     this.region = this.region || 'center'; // default..
36894     Roo.XComponent.register(this);
36895     this.modules = false;
36896     this.el = false; // where the layout goes..
36897     
36898     
36899 }
36900 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36901     /**
36902      * @property el
36903      * The created element (with Roo.factory())
36904      * @type {Roo.Layout}
36905      */
36906     el  : false,
36907     
36908     /**
36909      * @property el
36910      * for BC  - use el in new code
36911      * @type {Roo.Layout}
36912      */
36913     panel : false,
36914     
36915     /**
36916      * @property layout
36917      * for BC  - use el in new code
36918      * @type {Roo.Layout}
36919      */
36920     layout : false,
36921     
36922      /**
36923      * @cfg {Function|boolean} disabled
36924      * If this module is disabled by some rule, return true from the funtion
36925      */
36926     disabled : false,
36927     
36928     /**
36929      * @cfg {String} parent 
36930      * Name of parent element which it get xtype added to..
36931      */
36932     parent: false,
36933     
36934     /**
36935      * @cfg {String} order
36936      * Used to set the order in which elements are created (usefull for multiple tabs)
36937      */
36938     
36939     order : false,
36940     /**
36941      * @cfg {String} name
36942      * String to display while loading.
36943      */
36944     name : false,
36945     /**
36946      * @cfg {String} region
36947      * Region to render component to (defaults to center)
36948      */
36949     region : 'center',
36950     
36951     /**
36952      * @cfg {Array} items
36953      * A single item array - the first element is the root of the tree..
36954      * It's done this way to stay compatible with the Xtype system...
36955      */
36956     items : false,
36957     
36958     
36959      /**
36960      * render
36961      * render element to dom or tree
36962      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
36963      */
36964     
36965     render : function(el)
36966     {
36967         
36968         el = el || false;
36969         var hp = this.parent ? 1 : 0;
36970         
36971         if (!el && typeof(this.parent) == 'string' && this.parent[0] == '#') {
36972             // if parent is a '#.....' string, then let's use that..
36973             var ename = this.parent.substr(1)
36974             this.parent = false;
36975             el = Roo.get(ename);
36976             if (!el) {
36977                 Roo.log("Warning - element can not be found :#" + ename );
36978                 return;
36979             }
36980         }
36981         
36982         
36983         if (!this.parent) {
36984             
36985             el = el ? Roo.get(el) : false;
36986             
36987             // it's a top level one..
36988             this.parent =  {
36989                 el : new Roo.BorderLayout(el || document.body, {
36990                 
36991                      center: {
36992                          titlebar: false,
36993                          autoScroll:false,
36994                          closeOnTab: true,
36995                          tabPosition: 'top',
36996                           //resizeTabs: true,
36997                          alwaysShowTabs: el && hp? false :  true,
36998                          hideTabs: el || !hp ? true :  false,
36999                          minTabWidth: 140
37000                      }
37001                  })
37002             }
37003         }
37004         
37005         
37006             
37007         var tree = this.tree();
37008         tree.region = tree.region || this.region;
37009         this.el = this.parent.el.addxtype(tree);
37010         this.fireEvent('built', this);
37011         
37012         this.panel = this.el;
37013         this.layout = this.panel.layout;    
37014          
37015     }
37016     
37017 });
37018
37019 Roo.apply(Roo.XComponent, {
37020     
37021     /**
37022      * @property  buildCompleted
37023      * True when the builder has completed building the interface.
37024      * @type Boolean
37025      */
37026     buildCompleted : false,
37027      
37028     /**
37029      * @property  topModule
37030      * the upper most module - uses document.element as it's constructor.
37031      * @type Object
37032      */
37033      
37034     topModule  : false,
37035       
37036     /**
37037      * @property  modules
37038      * array of modules to be created by registration system.
37039      * @type {Array} of Roo.XComponent
37040      */
37041     
37042     modules : [],
37043     /**
37044      * @property  elmodules
37045      * array of modules to be created by which use #ID 
37046      * @type {Array} of Roo.XComponent
37047      */
37048      
37049     elmodules : [],
37050
37051     
37052     /**
37053      * Register components to be built later.
37054      *
37055      * This solves the following issues
37056      * - Building is not done on page load, but after an authentication process has occured.
37057      * - Interface elements are registered on page load
37058      * - Parent Interface elements may not be loaded before child, so this handles that..
37059      * 
37060      *
37061      * example:
37062      * 
37063      * MyApp.register({
37064           order : '000001',
37065           module : 'Pman.Tab.projectMgr',
37066           region : 'center',
37067           parent : 'Pman.layout',
37068           disabled : false,  // or use a function..
37069         })
37070      
37071      * * @param {Object} details about module
37072      */
37073     register : function(obj) {
37074         this.modules.push(obj);
37075          
37076     },
37077     /**
37078      * convert a string to an object..
37079      * eg. 'AAA.BBB' -> finds AAA.BBB
37080
37081      */
37082     
37083     toObject : function(str)
37084     {
37085         if (!str || typeof(str) == 'object') {
37086             return str;
37087         }
37088         if (str[0]=='#') {
37089             return str;
37090         }
37091
37092         var ar = str.split('.');
37093         var rt, o;
37094         rt = ar.shift();
37095             /** eval:var:o */
37096         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
37097         if (o === false) {
37098             throw "Module not found : " + str;
37099         }
37100         Roo.each(ar, function(e) {
37101             if (typeof(o[e]) == 'undefined') {
37102                 throw "Module not found : " + str;
37103             }
37104             o = o[e];
37105         });
37106         
37107         return o;
37108         
37109     },
37110     
37111     
37112     /**
37113      * move modules into their correct place in the tree..
37114      * 
37115      */
37116     preBuild : function ()
37117     {
37118         var _t = this;
37119         Roo.each(this.modules , function (obj)
37120         {
37121             var opar = obj.parent;
37122             obj.parent = this.toObject(opar);
37123             
37124             if (!obj.parent) {
37125                 this.topModule = obj;
37126                 return;
37127             }
37128             if (typeof(obj.parent) == 'string') {
37129                 this.elmodules.push(obj);
37130                 return;
37131             }
37132             if (obj.parent.constructor != Roo.XComponent) {
37133                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
37134             }
37135             if (!obj.parent.modules) {
37136                 obj.parent.modules = new Roo.util.MixedCollection(false, 
37137                     function(o) { return o.order + '' }
37138                 );
37139             }
37140             
37141             obj.parent.modules.add(obj);
37142         }, this);
37143     },
37144     
37145      /**
37146      * make a list of modules to build.
37147      * @return {Array} list of modules. 
37148      */ 
37149     
37150     buildOrder : function()
37151     {
37152         var _this = this;
37153         var cmp = function(a,b) {   
37154             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
37155         };
37156         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
37157             throw "No top level modules to build";
37158         }
37159         
37160         // make a flat list in order of modules to build.
37161         var mods = this.topModule ? [ this.topModule ] : [];
37162         Roo.each(this.elmodules,function(e) { mods.push(e) });
37163
37164         
37165         // add modules to their parents..
37166         var addMod = function(m) {
37167            // Roo.debug && Roo.log(m.modKey);
37168             
37169             mods.push(m);
37170             if (m.modules) {
37171                 m.modules.keySort('ASC',  cmp );
37172                 m.modules.each(addMod);
37173             }
37174             // not sure if this is used any more..
37175             if (m.finalize) {
37176                 m.finalize.name = m.name + " (clean up) ";
37177                 mods.push(m.finalize);
37178             }
37179             
37180         }
37181         if (this.topModule) { 
37182             this.topModule.modules.keySort('ASC',  cmp );
37183             this.topModule.modules.each(addMod);
37184         }
37185         return mods;
37186     },
37187     
37188      /**
37189      * Build the registered modules.
37190      * @param {Object} parent element.
37191      * @param {Function} optional method to call after module has been added.
37192      * 
37193      */ 
37194    
37195     build : function() 
37196     {
37197         
37198         this.preBuild();
37199         var mods = this.buildOrder();
37200       
37201         //this.allmods = mods;
37202         //Roo.debug && Roo.log(mods);
37203         //return;
37204         if (!mods.length) { // should not happen
37205             throw "NO modules!!!";
37206         }
37207         
37208         
37209         
37210         // flash it up as modal - so we store the mask!?
37211         Roo.MessageBox.show({ title: 'loading' });
37212         Roo.MessageBox.show({
37213            title: "Please wait...",
37214            msg: "Building Interface...",
37215            width:450,
37216            progress:true,
37217            closable:false,
37218            modal: false
37219           
37220         });
37221         var total = mods.length;
37222         
37223         var _this = this;
37224         var progressRun = function() {
37225             if (!mods.length) {
37226                 Roo.debug && Roo.log('hide?');
37227                 Roo.MessageBox.hide();
37228                 if (_this.topModule) { 
37229                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
37230                 }
37231                 // THE END...
37232                 return false;   
37233             }
37234             
37235             var m = mods.shift();
37236             
37237             
37238             Roo.debug && Roo.log(m);
37239             // not sure if this is supported any more.. - modules that are are just function
37240             if (typeof(m) == 'function') { 
37241                 m.call(this);
37242                 return progressRun.defer(10, _this);
37243             } 
37244             
37245             
37246             
37247             Roo.MessageBox.updateProgress(
37248                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
37249                     " of " + total + 
37250                     (m.name ? (' - ' + m.name) : '')
37251                     );
37252             
37253          
37254             // is the module disabled?
37255             var disabled = (typeof(m.disabled) == 'function') ?
37256                 m.disabled.call(m.module.disabled) : m.disabled;    
37257             
37258             
37259             if (disabled) {
37260                 return progressRun(); // we do not update the display!
37261             }
37262             
37263             // now build 
37264             
37265             m.render();
37266             // it's 10 on top level, and 1 on others??? why...
37267             return progressRun.defer(10, _this);
37268              
37269         }
37270         progressRun.defer(1, _this);
37271      
37272         
37273         
37274     }
37275     
37276      
37277    
37278     
37279     
37280 });
37281  //<script type="text/javascript">
37282
37283
37284 /**
37285  * @class Roo.Login
37286  * @extends Roo.LayoutDialog
37287  * A generic Login Dialog..... - only one needed in theory!?!?
37288  *
37289  * Fires XComponent builder on success...
37290  * 
37291  * Sends 
37292  *    username,password, lang = for login actions.
37293  *    check = 1 for periodic checking that sesion is valid.
37294  *    passwordRequest = email request password
37295  *    logout = 1 = to logout
37296  * 
37297  * Affects: (this id="????" elements)
37298  *   loading  (removed) (used to indicate application is loading)
37299  *   loading-mask (hides) (used to hide application when it's building loading)
37300  *   
37301  * 
37302  * Usage: 
37303  *    
37304  * 
37305  * Myapp.login = Roo.Login({
37306      url: xxxx,
37307    
37308      realm : 'Myapp', 
37309      
37310      
37311      method : 'POST',
37312      
37313      
37314      * 
37315  })
37316  * 
37317  * 
37318  * 
37319  **/
37320  
37321 Roo.Login = function(cfg)
37322 {
37323     this.addEvents({
37324         'refreshed' : true
37325     });
37326     
37327     Roo.apply(this,cfg);
37328     
37329     Roo.onReady(function() {
37330         this.onLoad();
37331     }, this);
37332     // call parent..
37333     
37334    
37335     Roo.Login.superclass.constructor.call(this, this);
37336     //this.addxtype(this.items[0]);
37337     
37338     
37339 }
37340
37341
37342 Roo.extend(Roo.Login, Roo.LayoutDialog, {
37343     
37344     /**
37345      * @cfg {String} method
37346      * Method used to query for login details.
37347      */
37348     
37349     method : 'POST',
37350     /**
37351      * @cfg {String} url
37352      * URL to query login data. - eg. baseURL + '/Login.php'
37353      */
37354     url : '',
37355     
37356     /**
37357      * @property user
37358      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
37359      * @type {Object} 
37360      */
37361     user : false,
37362     /**
37363      * @property checkFails
37364      * Number of times we have attempted to get authentication check, and failed.
37365      * @type {Number} 
37366      */
37367     checkFails : 0,
37368       /**
37369      * @property intervalID
37370      * The window interval that does the constant login checking.
37371      * @type {Number} 
37372      */
37373     intervalID : 0,
37374     
37375     
37376     onLoad : function() // called on page load...
37377     {
37378         // load 
37379          
37380         if (Roo.get('loading')) { // clear any loading indicator..
37381             Roo.get('loading').remove();
37382         }
37383         
37384         //this.switchLang('en'); // set the language to english..
37385        
37386         this.check({
37387             success:  function(response, opts)  {  // check successfull...
37388             
37389                 var res = this.processResponse(response);
37390                 this.checkFails =0;
37391                 if (!res.success) { // error!
37392                     this.checkFails = 5;
37393                     //console.log('call failure');
37394                     return this.failure(response,opts);
37395                 }
37396                 
37397                 if (!res.data.id) { // id=0 == login failure.
37398                     return this.show();
37399                 }
37400                 
37401                               
37402                         //console.log(success);
37403                 this.fillAuth(res.data);   
37404                 this.checkFails =0;
37405                 Roo.XComponent.build();
37406             },
37407             failure : this.show
37408         });
37409         
37410     }, 
37411     
37412     
37413     check: function(cfg) // called every so often to refresh cookie etc..
37414     {
37415         if (cfg.again) { // could be undefined..
37416             this.checkFails++;
37417         } else {
37418             this.checkFails = 0;
37419         }
37420         var _this = this;
37421         if (this.sending) {
37422             if ( this.checkFails > 4) {
37423                 Roo.MessageBox.alert("Error",  
37424                     "Error getting authentication status. - try reloading, or wait a while", function() {
37425                         _this.sending = false;
37426                     }); 
37427                 return;
37428             }
37429             cfg.again = true;
37430             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
37431             return;
37432         }
37433         this.sending = true;
37434         
37435         Roo.Ajax.request({  
37436             url: this.url,
37437             params: {
37438                 getAuthUser: true
37439             },  
37440             method: this.method,
37441             success:  cfg.success || this.success,
37442             failure : cfg.failure || this.failure,
37443             scope : this,
37444             callCfg : cfg
37445               
37446         });  
37447     }, 
37448     
37449     
37450     logout: function()
37451     {
37452         window.onbeforeunload = function() { }; // false does not work for IE..
37453         this.user = false;
37454         var _this = this;
37455         
37456         Roo.Ajax.request({  
37457             url: this.url,
37458             params: {
37459                 logout: 1
37460             },  
37461             method: 'GET',
37462             failure : function() {
37463                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
37464                     document.location = document.location.toString() + '?ts=' + Math.random();
37465                 });
37466                 
37467             },
37468             success : function() {
37469                 _this.user = false;
37470                 this.checkFails =0;
37471                 // fixme..
37472                 document.location = document.location.toString() + '?ts=' + Math.random();
37473             }
37474               
37475               
37476         }); 
37477     },
37478     
37479     processResponse : function (response)
37480     {
37481         var res = '';
37482         try {
37483             res = Roo.decode(response.responseText);
37484             // oops...
37485             if (typeof(res) != 'object') {
37486                 res = { success : false, errorMsg : res, errors : true };
37487             }
37488             if (typeof(res.success) == 'undefined') {
37489                 res.success = false;
37490             }
37491             
37492         } catch(e) {
37493             res = { success : false,  errorMsg : response.responseText, errors : true };
37494         }
37495         return res;
37496     },
37497     
37498     success : function(response, opts)  // check successfull...
37499     {  
37500         this.sending = false;
37501         var res = this.processResponse(response);
37502         if (!res.success) {
37503             return this.failure(response, opts);
37504         }
37505         if (!res.data || !res.data.id) {
37506             return this.failure(response,opts);
37507         }
37508         //console.log(res);
37509         this.fillAuth(res.data);
37510         
37511         this.checkFails =0;
37512         
37513     },
37514     
37515     
37516     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37517     {
37518         this.authUser = -1;
37519         this.sending = false;
37520         var res = this.processResponse(response);
37521         //console.log(res);
37522         if ( this.checkFails > 2) {
37523         
37524             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37525                 "Error getting authentication status. - try reloading"); 
37526             return;
37527         }
37528         opts.callCfg.again = true;
37529         this.check.defer(1000, this, [ opts.callCfg ]);
37530         return;  
37531     },
37532     
37533     
37534     
37535     fillAuth: function(au) {
37536         this.startAuthCheck();
37537         this.authUserId = au.id;
37538         this.authUser = au;
37539         this.lastChecked = new Date();
37540         this.fireEvent('refreshed', au);
37541         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37542         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37543         au.lang = au.lang || 'en';
37544         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37545         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37546         this.switchLang(au.lang );
37547         
37548      
37549         // open system... - -on setyp..
37550         if (this.authUserId  < 0) {
37551             Roo.MessageBox.alert("Warning", 
37552                 "This is an open system - please set up a admin user with a password.");  
37553         }
37554          
37555         //Pman.onload(); // which should do nothing if it's a re-auth result...
37556         
37557              
37558     },
37559     
37560     startAuthCheck : function() // starter for timeout checking..
37561     {
37562         if (this.intervalID) { // timer already in place...
37563             return false;
37564         }
37565         var _this = this;
37566         this.intervalID =  window.setInterval(function() {
37567               _this.check(false);
37568             }, 120000); // every 120 secs = 2mins..
37569         
37570         
37571     },
37572          
37573     
37574     switchLang : function (lang) 
37575     {
37576         _T = typeof(_T) == 'undefined' ? false : _T;
37577           if (!_T || !lang.length) {
37578             return;
37579         }
37580         
37581         if (!_T && lang != 'en') {
37582             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37583             return;
37584         }
37585         
37586         if (typeof(_T.en) == 'undefined') {
37587             _T.en = {};
37588             Roo.apply(_T.en, _T);
37589         }
37590         
37591         if (typeof(_T[lang]) == 'undefined') {
37592             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37593             return;
37594         }
37595         
37596         
37597         Roo.apply(_T, _T[lang]);
37598         // just need to set the text values for everything...
37599         var _this = this;
37600         /* this will not work ...
37601         if (this.form) { 
37602             
37603                
37604             function formLabel(name, val) {
37605                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37606             }
37607             
37608             formLabel('password', "Password"+':');
37609             formLabel('username', "Email Address"+':');
37610             formLabel('lang', "Language"+':');
37611             this.dialog.setTitle("Login");
37612             this.dialog.buttons[0].setText("Forgot Password");
37613             this.dialog.buttons[1].setText("Login");
37614         }
37615         */
37616         
37617         
37618     },
37619     
37620     
37621     title: "Login",
37622     modal: true,
37623     width:  350,
37624     //height: 230,
37625     height: 180,
37626     shadow: true,
37627     minWidth:200,
37628     minHeight:180,
37629     //proxyDrag: true,
37630     closable: false,
37631     draggable: false,
37632     collapsible: false,
37633     resizable: false,
37634     center: {  // needed??
37635         autoScroll:false,
37636         titlebar: false,
37637        // tabPosition: 'top',
37638         hideTabs: true,
37639         closeOnTab: true,
37640         alwaysShowTabs: false
37641     } ,
37642     listeners : {
37643         
37644         show  : function(dlg)
37645         {
37646             //console.log(this);
37647             this.form = this.layout.getRegion('center').activePanel.form;
37648             this.form.dialog = dlg;
37649             this.buttons[0].form = this.form;
37650             this.buttons[0].dialog = dlg;
37651             this.buttons[1].form = this.form;
37652             this.buttons[1].dialog = dlg;
37653            
37654            //this.resizeToLogo.defer(1000,this);
37655             // this is all related to resizing for logos..
37656             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37657            //// if (!sz) {
37658              //   this.resizeToLogo.defer(1000,this);
37659              //   return;
37660            // }
37661             //var w = Ext.lib.Dom.getViewWidth() - 100;
37662             //var h = Ext.lib.Dom.getViewHeight() - 100;
37663             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37664             //this.center();
37665             if (this.disabled) {
37666                 this.hide();
37667                 return;
37668             }
37669             
37670             if (this.user.id < 0) { // used for inital setup situations.
37671                 return;
37672             }
37673             
37674             if (this.intervalID) {
37675                 // remove the timer
37676                 window.clearInterval(this.intervalID);
37677                 this.intervalID = false;
37678             }
37679             
37680             
37681             if (Roo.get('loading')) {
37682                 Roo.get('loading').remove();
37683             }
37684             if (Roo.get('loading-mask')) {
37685                 Roo.get('loading-mask').hide();
37686             }
37687             
37688             //incomming._node = tnode;
37689             this.form.reset();
37690             //this.dialog.modal = !modal;
37691             //this.dialog.show();
37692             this.el.unmask(); 
37693             
37694             
37695             this.form.setValues({
37696                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37697                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37698             });
37699             
37700             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37701             if (this.form.findField('username').getValue().length > 0 ){
37702                 this.form.findField('password').focus();
37703             } else {
37704                this.form.findField('username').focus();
37705             }
37706     
37707         }
37708     },
37709     items : [
37710          {
37711        
37712             xtype : 'ContentPanel',
37713             xns : Roo,
37714             region: 'center',
37715             fitToFrame : true,
37716             
37717             items : [
37718     
37719                 {
37720                
37721                     xtype : 'Form',
37722                     xns : Roo.form,
37723                     labelWidth: 100,
37724                     style : 'margin: 10px;',
37725                     
37726                     listeners : {
37727                         actionfailed : function(f, act) {
37728                             // form can return { errors: .... }
37729                                 
37730                             //act.result.errors // invalid form element list...
37731                             //act.result.errorMsg// invalid form element list...
37732                             
37733                             this.dialog.el.unmask();
37734                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37735                                         "Login failed - communication error - try again.");
37736                                       
37737                         },
37738                         actioncomplete: function(re, act) {
37739                              
37740                             Roo.state.Manager.set(
37741                                 this.dialog.realm + '.username',  
37742                                     this.findField('username').getValue()
37743                             );
37744                             Roo.state.Manager.set(
37745                                 this.dialog.realm + '.lang',  
37746                                 this.findField('lang').getValue() 
37747                             );
37748                             
37749                             this.dialog.fillAuth(act.result.data);
37750                               
37751                             this.dialog.hide();
37752                             
37753                             if (Roo.get('loading-mask')) {
37754                                 Roo.get('loading-mask').show();
37755                             }
37756                             Roo.XComponent.build();
37757                             
37758                              
37759                             
37760                         }
37761                     },
37762                     items : [
37763                         {
37764                             xtype : 'TextField',
37765                             xns : Roo.form,
37766                             fieldLabel: "Email Address",
37767                             name: 'username',
37768                             width:200,
37769                             autoCreate : {tag: "input", type: "text", size: "20"}
37770                         },
37771                         {
37772                             xtype : 'TextField',
37773                             xns : Roo.form,
37774                             fieldLabel: "Password",
37775                             inputType: 'password',
37776                             name: 'password',
37777                             width:200,
37778                             autoCreate : {tag: "input", type: "text", size: "20"},
37779                             listeners : {
37780                                 specialkey : function(e,ev) {
37781                                     if (ev.keyCode == 13) {
37782                                         this.form.dialog.el.mask("Logging in");
37783                                         this.form.doAction('submit', {
37784                                             url: this.form.dialog.url,
37785                                             method: this.form.dialog.method
37786                                         });
37787                                     }
37788                                 }
37789                             }  
37790                         },
37791                         {
37792                             xtype : 'ComboBox',
37793                             xns : Roo.form,
37794                             fieldLabel: "Language",
37795                             name : 'langdisp',
37796                             store: {
37797                                 xtype : 'SimpleStore',
37798                                 fields: ['lang', 'ldisp'],
37799                                 data : [
37800                                     [ 'en', 'English' ],
37801                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37802                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37803                                 ]
37804                             },
37805                             
37806                             valueField : 'lang',
37807                             hiddenName:  'lang',
37808                             width: 200,
37809                             displayField:'ldisp',
37810                             typeAhead: false,
37811                             editable: false,
37812                             mode: 'local',
37813                             triggerAction: 'all',
37814                             emptyText:'Select a Language...',
37815                             selectOnFocus:true,
37816                             listeners : {
37817                                 select :  function(cb, rec, ix) {
37818                                     this.form.switchLang(rec.data.lang);
37819                                 }
37820                             }
37821                         
37822                         }
37823                     ]
37824                 }
37825                   
37826                 
37827             ]
37828         }
37829     ],
37830     buttons : [
37831         {
37832             xtype : 'Button',
37833             xns : 'Roo',
37834             text : "Forgot Password",
37835             listeners : {
37836                 click : function() {
37837                     //console.log(this);
37838                     var n = this.form.findField('username').getValue();
37839                     if (!n.length) {
37840                         Roo.MessageBox.alert("Error", "Fill in your email address");
37841                         return;
37842                     }
37843                     Roo.Ajax.request({
37844                         url: this.dialog.url,
37845                         params: {
37846                             passwordRequest: n
37847                         },
37848                         method: this.dialog.method,
37849                         success:  function(response, opts)  {  // check successfull...
37850                         
37851                             var res = this.dialog.processResponse(response);
37852                             if (!res.success) { // error!
37853                                Roo.MessageBox.alert("Error" ,
37854                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37855                                return;
37856                             }
37857                             Roo.MessageBox.alert("Notice" ,
37858                                 "Please check you email for the Password Reset message");
37859                         },
37860                         failure : function() {
37861                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37862                         }
37863                         
37864                     });
37865                 }
37866             }
37867         },
37868         {
37869             xtype : 'Button',
37870             xns : 'Roo',
37871             text : "Login",
37872             listeners : {
37873                 
37874                 click : function () {
37875                         
37876                     this.dialog.el.mask("Logging in");
37877                     this.form.doAction('submit', {
37878                             url: this.dialog.url,
37879                             method: this.dialog.method
37880                     });
37881                 }
37882             }
37883         }
37884     ]
37885   
37886   
37887 })
37888  
37889
37890
37891