roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75 };
76
77 Roo.dd.DragDrop.prototype = {
78
79     /**
80      * The id of the element associated with this object.  This is what we
81      * refer to as the "linked element" because the size and position of
82      * this element is used to determine when the drag and drop objects have
83      * interacted.
84      * @property id
85      * @type String
86      */
87     id: null,
88
89     /**
90      * Configuration attributes passed into the constructor
91      * @property config
92      * @type object
93      */
94     config: null,
95
96     /**
97      * The id of the element that will be dragged.  By default this is same
98      * as the linked element , but could be changed to another element. Ex:
99      * Roo.dd.DDProxy
100      * @property dragElId
101      * @type String
102      * @private
103      */
104     dragElId: null,
105
106     /**
107      * the id of the element that initiates the drag operation.  By default
108      * this is the linked element, but could be changed to be a child of this
109      * element.  This lets us do things like only starting the drag when the
110      * header element within the linked html element is clicked.
111      * @property handleElId
112      * @type String
113      * @private
114      */
115     handleElId: null,
116
117     /**
118      * An associative array of HTML tags that will be ignored if clicked.
119      * @property invalidHandleTypes
120      * @type {string: string}
121      */
122     invalidHandleTypes: null,
123
124     /**
125      * An associative array of ids for elements that will be ignored if clicked
126      * @property invalidHandleIds
127      * @type {string: string}
128      */
129     invalidHandleIds: null,
130
131     /**
132      * An indexted array of css class names for elements that will be ignored
133      * if clicked.
134      * @property invalidHandleClasses
135      * @type string[]
136      */
137     invalidHandleClasses: null,
138
139     /**
140      * The linked element's absolute X position at the time the drag was
141      * started
142      * @property startPageX
143      * @type int
144      * @private
145      */
146     startPageX: 0,
147
148     /**
149      * The linked element's absolute X position at the time the drag was
150      * started
151      * @property startPageY
152      * @type int
153      * @private
154      */
155     startPageY: 0,
156
157     /**
158      * The group defines a logical collection of DragDrop objects that are
159      * related.  Instances only get events when interacting with other
160      * DragDrop object in the same group.  This lets us define multiple
161      * groups using a single DragDrop subclass if we want.
162      * @property groups
163      * @type {string: string}
164      */
165     groups: null,
166
167     /**
168      * Individual drag/drop instances can be locked.  This will prevent
169      * onmousedown start drag.
170      * @property locked
171      * @type boolean
172      * @private
173      */
174     locked: false,
175
176     /**
177      * Lock this instance
178      * @method lock
179      */
180     lock: function() { this.locked = true; },
181
182     /**
183      * Unlock this instace
184      * @method unlock
185      */
186     unlock: function() { this.locked = false; },
187
188     /**
189      * By default, all insances can be a drop target.  This can be disabled by
190      * setting isTarget to false.
191      * @method isTarget
192      * @type boolean
193      */
194     isTarget: true,
195
196     /**
197      * The padding configured for this drag and drop object for calculating
198      * the drop zone intersection with this object.
199      * @method padding
200      * @type int[]
201      */
202     padding: null,
203
204     /**
205      * Cached reference to the linked element
206      * @property _domRef
207      * @private
208      */
209     _domRef: null,
210
211     /**
212      * Internal typeof flag
213      * @property __ygDragDrop
214      * @private
215      */
216     __ygDragDrop: true,
217
218     /**
219      * Set to true when horizontal contraints are applied
220      * @property constrainX
221      * @type boolean
222      * @private
223      */
224     constrainX: false,
225
226     /**
227      * Set to true when vertical contraints are applied
228      * @property constrainY
229      * @type boolean
230      * @private
231      */
232     constrainY: false,
233
234     /**
235      * The left constraint
236      * @property minX
237      * @type int
238      * @private
239      */
240     minX: 0,
241
242     /**
243      * The right constraint
244      * @property maxX
245      * @type int
246      * @private
247      */
248     maxX: 0,
249
250     /**
251      * The up constraint
252      * @property minY
253      * @type int
254      * @type int
255      * @private
256      */
257     minY: 0,
258
259     /**
260      * The down constraint
261      * @property maxY
262      * @type int
263      * @private
264      */
265     maxY: 0,
266
267     /**
268      * Maintain offsets when we resetconstraints.  Set to true when you want
269      * the position of the element relative to its parent to stay the same
270      * when the page changes
271      *
272      * @property maintainOffset
273      * @type boolean
274      */
275     maintainOffset: false,
276
277     /**
278      * Array of pixel locations the element will snap to if we specified a
279      * horizontal graduation/interval.  This array is generated automatically
280      * when you define a tick interval.
281      * @property xTicks
282      * @type int[]
283      */
284     xTicks: null,
285
286     /**
287      * Array of pixel locations the element will snap to if we specified a
288      * vertical graduation/interval.  This array is generated automatically
289      * when you define a tick interval.
290      * @property yTicks
291      * @type int[]
292      */
293     yTicks: null,
294
295     /**
296      * By default the drag and drop instance will only respond to the primary
297      * button click (left button for a right-handed mouse).  Set to true to
298      * allow drag and drop to start with any mouse click that is propogated
299      * by the browser
300      * @property primaryButtonOnly
301      * @type boolean
302      */
303     primaryButtonOnly: true,
304
305     /**
306      * The availabe property is false until the linked dom element is accessible.
307      * @property available
308      * @type boolean
309      */
310     available: false,
311
312     /**
313      * By default, drags can only be initiated if the mousedown occurs in the
314      * region the linked element is.  This is done in part to work around a
315      * bug in some browsers that mis-report the mousedown if the previous
316      * mouseup happened outside of the window.  This property is set to true
317      * if outer handles are defined.
318      *
319      * @property hasOuterHandles
320      * @type boolean
321      * @default false
322      */
323     hasOuterHandles: false,
324
325     /**
326      * Code that executes immediately before the startDrag event
327      * @method b4StartDrag
328      * @private
329      */
330     b4StartDrag: function(x, y) { },
331
332     /**
333      * Abstract method called after a drag/drop object is clicked
334      * and the drag or mousedown time thresholds have beeen met.
335      * @method startDrag
336      * @param {int} X click location
337      * @param {int} Y click location
338      */
339     startDrag: function(x, y) { /* override this */ },
340
341     /**
342      * Code that executes immediately before the onDrag event
343      * @method b4Drag
344      * @private
345      */
346     b4Drag: function(e) { },
347
348     /**
349      * Abstract method called during the onMouseMove event while dragging an
350      * object.
351      * @method onDrag
352      * @param {Event} e the mousemove event
353      */
354     onDrag: function(e) { /* override this */ },
355
356     /**
357      * Abstract method called when this element fist begins hovering over
358      * another DragDrop obj
359      * @method onDragEnter
360      * @param {Event} e the mousemove event
361      * @param {String|DragDrop[]} id In POINT mode, the element
362      * id this is hovering over.  In INTERSECT mode, an array of one or more
363      * dragdrop items being hovered over.
364      */
365     onDragEnter: function(e, id) { /* override this */ },
366
367     /**
368      * Code that executes immediately before the onDragOver event
369      * @method b4DragOver
370      * @private
371      */
372     b4DragOver: function(e) { },
373
374     /**
375      * Abstract method called when this element is hovering over another
376      * DragDrop obj
377      * @method onDragOver
378      * @param {Event} e the mousemove event
379      * @param {String|DragDrop[]} id In POINT mode, the element
380      * id this is hovering over.  In INTERSECT mode, an array of dd items
381      * being hovered over.
382      */
383     onDragOver: function(e, id) { /* override this */ },
384
385     /**
386      * Code that executes immediately before the onDragOut event
387      * @method b4DragOut
388      * @private
389      */
390     b4DragOut: function(e) { },
391
392     /**
393      * Abstract method called when we are no longer hovering over an element
394      * @method onDragOut
395      * @param {Event} e the mousemove event
396      * @param {String|DragDrop[]} id In POINT mode, the element
397      * id this was hovering over.  In INTERSECT mode, an array of dd items
398      * that the mouse is no longer over.
399      */
400     onDragOut: function(e, id) { /* override this */ },
401
402     /**
403      * Code that executes immediately before the onDragDrop event
404      * @method b4DragDrop
405      * @private
406      */
407     b4DragDrop: function(e) { },
408
409     /**
410      * Abstract method called when this item is dropped on another DragDrop
411      * obj
412      * @method onDragDrop
413      * @param {Event} e the mouseup event
414      * @param {String|DragDrop[]} id In POINT mode, the element
415      * id this was dropped on.  In INTERSECT mode, an array of dd items this
416      * was dropped on.
417      */
418     onDragDrop: function(e, id) { /* override this */ },
419
420     /**
421      * Abstract method called when this item is dropped on an area with no
422      * drop target
423      * @method onInvalidDrop
424      * @param {Event} e the mouseup event
425      */
426     onInvalidDrop: function(e) { /* override this */ },
427
428     /**
429      * Code that executes immediately before the endDrag event
430      * @method b4EndDrag
431      * @private
432      */
433     b4EndDrag: function(e) { },
434
435     /**
436      * Fired when we are done dragging the object
437      * @method endDrag
438      * @param {Event} e the mouseup event
439      */
440     endDrag: function(e) { /* override this */ },
441
442     /**
443      * Code executed immediately before the onMouseDown event
444      * @method b4MouseDown
445      * @param {Event} e the mousedown event
446      * @private
447      */
448     b4MouseDown: function(e) {  },
449
450     /**
451      * Event handler that fires when a drag/drop obj gets a mousedown
452      * @method onMouseDown
453      * @param {Event} e the mousedown event
454      */
455     onMouseDown: function(e) { /* override this */ },
456
457     /**
458      * Event handler that fires when a drag/drop obj gets a mouseup
459      * @method onMouseUp
460      * @param {Event} e the mouseup event
461      */
462     onMouseUp: function(e) { /* override this */ },
463
464     /**
465      * Override the onAvailable method to do what is needed after the initial
466      * position was determined.
467      * @method onAvailable
468      */
469     onAvailable: function () {
470     },
471
472     /*
473      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
474      * @type Object
475      */
476     defaultPadding : {left:0, right:0, top:0, bottom:0},
477
478     /*
479      * Initializes the drag drop object's constraints to restrict movement to a certain element.
480  *
481  * Usage:
482  <pre><code>
483  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
484                 { dragElId: "existingProxyDiv" });
485  dd.startDrag = function(){
486      this.constrainTo("parent-id");
487  };
488  </code></pre>
489  * Or you can initalize it using the {@link Roo.Element} object:
490  <pre><code>
491  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
492      startDrag : function(){
493          this.constrainTo("parent-id");
494      }
495  });
496  </code></pre>
497      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
498      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
499      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
500      * an object containing the sides to pad. For example: {right:10, bottom:10}
501      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
502      */
503     constrainTo : function(constrainTo, pad, inContent){
504         if(typeof pad == "number"){
505             pad = {left: pad, right:pad, top:pad, bottom:pad};
506         }
507         pad = pad || this.defaultPadding;
508         var b = Roo.get(this.getEl()).getBox();
509         var ce = Roo.get(constrainTo);
510         var s = ce.getScroll();
511         var c, cd = ce.dom;
512         if(cd == document.body){
513             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
514         }else{
515             xy = ce.getXY();
516             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
517         }
518
519
520         var topSpace = b.y - c.y;
521         var leftSpace = b.x - c.x;
522
523         this.resetConstraints();
524         this.setXConstraint(leftSpace - (pad.left||0), // left
525                 c.width - leftSpace - b.width - (pad.right||0) //right
526         );
527         this.setYConstraint(topSpace - (pad.top||0), //top
528                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
529         );
530     },
531
532     /**
533      * Returns a reference to the linked element
534      * @method getEl
535      * @return {HTMLElement} the html element
536      */
537     getEl: function() {
538         if (!this._domRef) {
539             this._domRef = Roo.getDom(this.id);
540         }
541
542         return this._domRef;
543     },
544
545     /**
546      * Returns a reference to the actual element to drag.  By default this is
547      * the same as the html element, but it can be assigned to another
548      * element. An example of this can be found in Roo.dd.DDProxy
549      * @method getDragEl
550      * @return {HTMLElement} the html element
551      */
552     getDragEl: function() {
553         return Roo.getDom(this.dragElId);
554     },
555
556     /**
557      * Sets up the DragDrop object.  Must be called in the constructor of any
558      * Roo.dd.DragDrop subclass
559      * @method init
560      * @param id the id of the linked element
561      * @param {String} sGroup the group of related items
562      * @param {object} config configuration attributes
563      */
564     init: function(id, sGroup, config) {
565         this.initTarget(id, sGroup, config);
566         Event.on(this.id, "mousedown", this.handleMouseDown, this);
567         // Event.on(this.id, "selectstart", Event.preventDefault);
568     },
569
570     /**
571      * Initializes Targeting functionality only... the object does not
572      * get a mousedown handler.
573      * @method initTarget
574      * @param id the id of the linked element
575      * @param {String} sGroup the group of related items
576      * @param {object} config configuration attributes
577      */
578     initTarget: function(id, sGroup, config) {
579
580         // configuration attributes
581         this.config = config || {};
582
583         // create a local reference to the drag and drop manager
584         this.DDM = Roo.dd.DDM;
585         // initialize the groups array
586         this.groups = {};
587
588         // assume that we have an element reference instead of an id if the
589         // parameter is not a string
590         if (typeof id !== "string") {
591             id = Roo.id(id);
592         }
593
594         // set the id
595         this.id = id;
596
597         // add to an interaction group
598         this.addToGroup((sGroup) ? sGroup : "default");
599
600         // We don't want to register this as the handle with the manager
601         // so we just set the id rather than calling the setter.
602         this.handleElId = id;
603
604         // the linked element is the element that gets dragged by default
605         this.setDragElId(id);
606
607         // by default, clicked anchors will not start drag operations.
608         this.invalidHandleTypes = { A: "A" };
609         this.invalidHandleIds = {};
610         this.invalidHandleClasses = [];
611
612         this.applyConfig();
613
614         this.handleOnAvailable();
615     },
616
617     /**
618      * Applies the configuration parameters that were passed into the constructor.
619      * This is supposed to happen at each level through the inheritance chain.  So
620      * a DDProxy implentation will execute apply config on DDProxy, DD, and
621      * DragDrop in order to get all of the parameters that are available in
622      * each object.
623      * @method applyConfig
624      */
625     applyConfig: function() {
626
627         // configurable properties:
628         //    padding, isTarget, maintainOffset, primaryButtonOnly
629         this.padding           = this.config.padding || [0, 0, 0, 0];
630         this.isTarget          = (this.config.isTarget !== false);
631         this.maintainOffset    = (this.config.maintainOffset);
632         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
633
634     },
635
636     /**
637      * Executed when the linked element is available
638      * @method handleOnAvailable
639      * @private
640      */
641     handleOnAvailable: function() {
642         this.available = true;
643         this.resetConstraints();
644         this.onAvailable();
645     },
646
647      /**
648      * Configures the padding for the target zone in px.  Effectively expands
649      * (or reduces) the virtual object size for targeting calculations.
650      * Supports css-style shorthand; if only one parameter is passed, all sides
651      * will have that padding, and if only two are passed, the top and bottom
652      * will have the first param, the left and right the second.
653      * @method setPadding
654      * @param {int} iTop    Top pad
655      * @param {int} iRight  Right pad
656      * @param {int} iBot    Bot pad
657      * @param {int} iLeft   Left pad
658      */
659     setPadding: function(iTop, iRight, iBot, iLeft) {
660         // this.padding = [iLeft, iRight, iTop, iBot];
661         if (!iRight && 0 !== iRight) {
662             this.padding = [iTop, iTop, iTop, iTop];
663         } else if (!iBot && 0 !== iBot) {
664             this.padding = [iTop, iRight, iTop, iRight];
665         } else {
666             this.padding = [iTop, iRight, iBot, iLeft];
667         }
668     },
669
670     /**
671      * Stores the initial placement of the linked element.
672      * @method setInitialPosition
673      * @param {int} diffX   the X offset, default 0
674      * @param {int} diffY   the Y offset, default 0
675      */
676     setInitPosition: function(diffX, diffY) {
677         var el = this.getEl();
678
679         if (!this.DDM.verifyEl(el)) {
680             return;
681         }
682
683         var dx = diffX || 0;
684         var dy = diffY || 0;
685
686         var p = Dom.getXY( el );
687
688         this.initPageX = p[0] - dx;
689         this.initPageY = p[1] - dy;
690
691         this.lastPageX = p[0];
692         this.lastPageY = p[1];
693
694
695         this.setStartPosition(p);
696     },
697
698     /**
699      * Sets the start position of the element.  This is set when the obj
700      * is initialized, the reset when a drag is started.
701      * @method setStartPosition
702      * @param pos current position (from previous lookup)
703      * @private
704      */
705     setStartPosition: function(pos) {
706         var p = pos || Dom.getXY( this.getEl() );
707         this.deltaSetXY = null;
708
709         this.startPageX = p[0];
710         this.startPageY = p[1];
711     },
712
713     /**
714      * Add this instance to a group of related drag/drop objects.  All
715      * instances belong to at least one group, and can belong to as many
716      * groups as needed.
717      * @method addToGroup
718      * @param sGroup {string} the name of the group
719      */
720     addToGroup: function(sGroup) {
721         this.groups[sGroup] = true;
722         this.DDM.regDragDrop(this, sGroup);
723     },
724
725     /**
726      * Remove's this instance from the supplied interaction group
727      * @method removeFromGroup
728      * @param {string}  sGroup  The group to drop
729      */
730     removeFromGroup: function(sGroup) {
731         if (this.groups[sGroup]) {
732             delete this.groups[sGroup];
733         }
734
735         this.DDM.removeDDFromGroup(this, sGroup);
736     },
737
738     /**
739      * Allows you to specify that an element other than the linked element
740      * will be moved with the cursor during a drag
741      * @method setDragElId
742      * @param id {string} the id of the element that will be used to initiate the drag
743      */
744     setDragElId: function(id) {
745         this.dragElId = id;
746     },
747
748     /**
749      * Allows you to specify a child of the linked element that should be
750      * used to initiate the drag operation.  An example of this would be if
751      * you have a content div with text and links.  Clicking anywhere in the
752      * content area would normally start the drag operation.  Use this method
753      * to specify that an element inside of the content div is the element
754      * that starts the drag operation.
755      * @method setHandleElId
756      * @param id {string} the id of the element that will be used to
757      * initiate the drag.
758      */
759     setHandleElId: function(id) {
760         if (typeof id !== "string") {
761             id = Roo.id(id);
762         }
763         this.handleElId = id;
764         this.DDM.regHandle(this.id, id);
765     },
766
767     /**
768      * Allows you to set an element outside of the linked element as a drag
769      * handle
770      * @method setOuterHandleElId
771      * @param id the id of the element that will be used to initiate the drag
772      */
773     setOuterHandleElId: function(id) {
774         if (typeof id !== "string") {
775             id = Roo.id(id);
776         }
777         Event.on(id, "mousedown",
778                 this.handleMouseDown, this);
779         this.setHandleElId(id);
780
781         this.hasOuterHandles = true;
782     },
783
784     /**
785      * Remove all drag and drop hooks for this element
786      * @method unreg
787      */
788     unreg: function() {
789         Event.un(this.id, "mousedown",
790                 this.handleMouseDown);
791         this._domRef = null;
792         this.DDM._remove(this);
793     },
794
795     destroy : function(){
796         this.unreg();
797     },
798
799     /**
800      * Returns true if this instance is locked, or the drag drop mgr is locked
801      * (meaning that all drag/drop is disabled on the page.)
802      * @method isLocked
803      * @return {boolean} true if this obj or all drag/drop is locked, else
804      * false
805      */
806     isLocked: function() {
807         return (this.DDM.isLocked() || this.locked);
808     },
809
810     /**
811      * Fired when this object is clicked
812      * @method handleMouseDown
813      * @param {Event} e
814      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
815      * @private
816      */
817     handleMouseDown: function(e, oDD){
818         if (this.primaryButtonOnly && e.button != 0) {
819             return;
820         }
821
822         if (this.isLocked()) {
823             return;
824         }
825
826         this.DDM.refreshCache(this.groups);
827
828         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
829         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
830         } else {
831             if (this.clickValidator(e)) {
832
833                 // set the initial element position
834                 this.setStartPosition();
835
836
837                 this.b4MouseDown(e);
838                 this.onMouseDown(e);
839
840                 this.DDM.handleMouseDown(e, this);
841
842                 this.DDM.stopEvent(e);
843             } else {
844
845
846             }
847         }
848     },
849
850     clickValidator: function(e) {
851         var target = e.getTarget();
852         return ( this.isValidHandleChild(target) &&
853                     (this.id == this.handleElId ||
854                         this.DDM.handleWasClicked(target, this.id)) );
855     },
856
857     /**
858      * Allows you to specify a tag name that should not start a drag operation
859      * when clicked.  This is designed to facilitate embedding links within a
860      * drag handle that do something other than start the drag.
861      * @method addInvalidHandleType
862      * @param {string} tagName the type of element to exclude
863      */
864     addInvalidHandleType: function(tagName) {
865         var type = tagName.toUpperCase();
866         this.invalidHandleTypes[type] = type;
867     },
868
869     /**
870      * Lets you to specify an element id for a child of a drag handle
871      * that should not initiate a drag
872      * @method addInvalidHandleId
873      * @param {string} id the element id of the element you wish to ignore
874      */
875     addInvalidHandleId: function(id) {
876         if (typeof id !== "string") {
877             id = Roo.id(id);
878         }
879         this.invalidHandleIds[id] = id;
880     },
881
882     /**
883      * Lets you specify a css class of elements that will not initiate a drag
884      * @method addInvalidHandleClass
885      * @param {string} cssClass the class of the elements you wish to ignore
886      */
887     addInvalidHandleClass: function(cssClass) {
888         this.invalidHandleClasses.push(cssClass);
889     },
890
891     /**
892      * Unsets an excluded tag name set by addInvalidHandleType
893      * @method removeInvalidHandleType
894      * @param {string} tagName the type of element to unexclude
895      */
896     removeInvalidHandleType: function(tagName) {
897         var type = tagName.toUpperCase();
898         // this.invalidHandleTypes[type] = null;
899         delete this.invalidHandleTypes[type];
900     },
901
902     /**
903      * Unsets an invalid handle id
904      * @method removeInvalidHandleId
905      * @param {string} id the id of the element to re-enable
906      */
907     removeInvalidHandleId: function(id) {
908         if (typeof id !== "string") {
909             id = Roo.id(id);
910         }
911         delete this.invalidHandleIds[id];
912     },
913
914     /**
915      * Unsets an invalid css class
916      * @method removeInvalidHandleClass
917      * @param {string} cssClass the class of the element(s) you wish to
918      * re-enable
919      */
920     removeInvalidHandleClass: function(cssClass) {
921         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
922             if (this.invalidHandleClasses[i] == cssClass) {
923                 delete this.invalidHandleClasses[i];
924             }
925         }
926     },
927
928     /**
929      * Checks the tag exclusion list to see if this click should be ignored
930      * @method isValidHandleChild
931      * @param {HTMLElement} node the HTMLElement to evaluate
932      * @return {boolean} true if this is a valid tag type, false if not
933      */
934     isValidHandleChild: function(node) {
935
936         var valid = true;
937         // var n = (node.nodeName == "#text") ? node.parentNode : node;
938         var nodeName;
939         try {
940             nodeName = node.nodeName.toUpperCase();
941         } catch(e) {
942             nodeName = node.nodeName;
943         }
944         valid = valid && !this.invalidHandleTypes[nodeName];
945         valid = valid && !this.invalidHandleIds[node.id];
946
947         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
948             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
949         }
950
951
952         return valid;
953
954     },
955
956     /**
957      * Create the array of horizontal tick marks if an interval was specified
958      * in setXConstraint().
959      * @method setXTicks
960      * @private
961      */
962     setXTicks: function(iStartX, iTickSize) {
963         this.xTicks = [];
964         this.xTickSize = iTickSize;
965
966         var tickMap = {};
967
968         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
969             if (!tickMap[i]) {
970                 this.xTicks[this.xTicks.length] = i;
971                 tickMap[i] = true;
972             }
973         }
974
975         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
976             if (!tickMap[i]) {
977                 this.xTicks[this.xTicks.length] = i;
978                 tickMap[i] = true;
979             }
980         }
981
982         this.xTicks.sort(this.DDM.numericSort) ;
983     },
984
985     /**
986      * Create the array of vertical tick marks if an interval was specified in
987      * setYConstraint().
988      * @method setYTicks
989      * @private
990      */
991     setYTicks: function(iStartY, iTickSize) {
992         this.yTicks = [];
993         this.yTickSize = iTickSize;
994
995         var tickMap = {};
996
997         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
998             if (!tickMap[i]) {
999                 this.yTicks[this.yTicks.length] = i;
1000                 tickMap[i] = true;
1001             }
1002         }
1003
1004         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1005             if (!tickMap[i]) {
1006                 this.yTicks[this.yTicks.length] = i;
1007                 tickMap[i] = true;
1008             }
1009         }
1010
1011         this.yTicks.sort(this.DDM.numericSort) ;
1012     },
1013
1014     /**
1015      * By default, the element can be dragged any place on the screen.  Use
1016      * this method to limit the horizontal travel of the element.  Pass in
1017      * 0,0 for the parameters if you want to lock the drag to the y axis.
1018      * @method setXConstraint
1019      * @param {int} iLeft the number of pixels the element can move to the left
1020      * @param {int} iRight the number of pixels the element can move to the
1021      * right
1022      * @param {int} iTickSize optional parameter for specifying that the
1023      * element
1024      * should move iTickSize pixels at a time.
1025      */
1026     setXConstraint: function(iLeft, iRight, iTickSize) {
1027         this.leftConstraint = iLeft;
1028         this.rightConstraint = iRight;
1029
1030         this.minX = this.initPageX - iLeft;
1031         this.maxX = this.initPageX + iRight;
1032         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1033
1034         this.constrainX = true;
1035     },
1036
1037     /**
1038      * Clears any constraints applied to this instance.  Also clears ticks
1039      * since they can't exist independent of a constraint at this time.
1040      * @method clearConstraints
1041      */
1042     clearConstraints: function() {
1043         this.constrainX = false;
1044         this.constrainY = false;
1045         this.clearTicks();
1046     },
1047
1048     /**
1049      * Clears any tick interval defined for this instance
1050      * @method clearTicks
1051      */
1052     clearTicks: function() {
1053         this.xTicks = null;
1054         this.yTicks = null;
1055         this.xTickSize = 0;
1056         this.yTickSize = 0;
1057     },
1058
1059     /**
1060      * By default, the element can be dragged any place on the screen.  Set
1061      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1062      * parameters if you want to lock the drag to the x axis.
1063      * @method setYConstraint
1064      * @param {int} iUp the number of pixels the element can move up
1065      * @param {int} iDown the number of pixels the element can move down
1066      * @param {int} iTickSize optional parameter for specifying that the
1067      * element should move iTickSize pixels at a time.
1068      */
1069     setYConstraint: function(iUp, iDown, iTickSize) {
1070         this.topConstraint = iUp;
1071         this.bottomConstraint = iDown;
1072
1073         this.minY = this.initPageY - iUp;
1074         this.maxY = this.initPageY + iDown;
1075         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1076
1077         this.constrainY = true;
1078
1079     },
1080
1081     /**
1082      * resetConstraints must be called if you manually reposition a dd element.
1083      * @method resetConstraints
1084      * @param {boolean} maintainOffset
1085      */
1086     resetConstraints: function() {
1087
1088
1089         // Maintain offsets if necessary
1090         if (this.initPageX || this.initPageX === 0) {
1091             // figure out how much this thing has moved
1092             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1093             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1094
1095             this.setInitPosition(dx, dy);
1096
1097         // This is the first time we have detected the element's position
1098         } else {
1099             this.setInitPosition();
1100         }
1101
1102         if (this.constrainX) {
1103             this.setXConstraint( this.leftConstraint,
1104                                  this.rightConstraint,
1105                                  this.xTickSize        );
1106         }
1107
1108         if (this.constrainY) {
1109             this.setYConstraint( this.topConstraint,
1110                                  this.bottomConstraint,
1111                                  this.yTickSize         );
1112         }
1113     },
1114
1115     /**
1116      * Normally the drag element is moved pixel by pixel, but we can specify
1117      * that it move a number of pixels at a time.  This method resolves the
1118      * location when we have it set up like this.
1119      * @method getTick
1120      * @param {int} val where we want to place the object
1121      * @param {int[]} tickArray sorted array of valid points
1122      * @return {int} the closest tick
1123      * @private
1124      */
1125     getTick: function(val, tickArray) {
1126
1127         if (!tickArray) {
1128             // If tick interval is not defined, it is effectively 1 pixel,
1129             // so we return the value passed to us.
1130             return val;
1131         } else if (tickArray[0] >= val) {
1132             // The value is lower than the first tick, so we return the first
1133             // tick.
1134             return tickArray[0];
1135         } else {
1136             for (var i=0, len=tickArray.length; i<len; ++i) {
1137                 var next = i + 1;
1138                 if (tickArray[next] && tickArray[next] >= val) {
1139                     var diff1 = val - tickArray[i];
1140                     var diff2 = tickArray[next] - val;
1141                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1142                 }
1143             }
1144
1145             // The value is larger than the last tick, so we return the last
1146             // tick.
1147             return tickArray[tickArray.length - 1];
1148         }
1149     },
1150
1151     /**
1152      * toString method
1153      * @method toString
1154      * @return {string} string representation of the dd obj
1155      */
1156     toString: function() {
1157         return ("DragDrop " + this.id);
1158     }
1159
1160 };
1161
1162 })();
1163 /*
1164  * Based on:
1165  * Ext JS Library 1.1.1
1166  * Copyright(c) 2006-2007, Ext JS, LLC.
1167  *
1168  * Originally Released Under LGPL - original licence link has changed is not relivant.
1169  *
1170  * Fork - LGPL
1171  * <script type="text/javascript">
1172  */
1173
1174
1175 /**
1176  * The drag and drop utility provides a framework for building drag and drop
1177  * applications.  In addition to enabling drag and drop for specific elements,
1178  * the drag and drop elements are tracked by the manager class, and the
1179  * interactions between the various elements are tracked during the drag and
1180  * the implementing code is notified about these important moments.
1181  */
1182
1183 // Only load the library once.  Rewriting the manager class would orphan
1184 // existing drag and drop instances.
1185 if (!Roo.dd.DragDropMgr) {
1186
1187 /**
1188  * @class Roo.dd.DragDropMgr
1189  * DragDropMgr is a singleton that tracks the element interaction for
1190  * all DragDrop items in the window.  Generally, you will not call
1191  * this class directly, but it does have helper methods that could
1192  * be useful in your DragDrop implementations.
1193  * @singleton
1194  */
1195 Roo.dd.DragDropMgr = function() {
1196
1197     var Event = Roo.EventManager;
1198
1199     return {
1200
1201         /**
1202          * Two dimensional Array of registered DragDrop objects.  The first
1203          * dimension is the DragDrop item group, the second the DragDrop
1204          * object.
1205          * @property ids
1206          * @type {string: string}
1207          * @private
1208          * @static
1209          */
1210         ids: {},
1211
1212         /**
1213          * Array of element ids defined as drag handles.  Used to determine
1214          * if the element that generated the mousedown event is actually the
1215          * handle and not the html element itself.
1216          * @property handleIds
1217          * @type {string: string}
1218          * @private
1219          * @static
1220          */
1221         handleIds: {},
1222
1223         /**
1224          * the DragDrop object that is currently being dragged
1225          * @property dragCurrent
1226          * @type DragDrop
1227          * @private
1228          * @static
1229          **/
1230         dragCurrent: null,
1231
1232         /**
1233          * the DragDrop object(s) that are being hovered over
1234          * @property dragOvers
1235          * @type Array
1236          * @private
1237          * @static
1238          */
1239         dragOvers: {},
1240
1241         /**
1242          * the X distance between the cursor and the object being dragged
1243          * @property deltaX
1244          * @type int
1245          * @private
1246          * @static
1247          */
1248         deltaX: 0,
1249
1250         /**
1251          * the Y distance between the cursor and the object being dragged
1252          * @property deltaY
1253          * @type int
1254          * @private
1255          * @static
1256          */
1257         deltaY: 0,
1258
1259         /**
1260          * Flag to determine if we should prevent the default behavior of the
1261          * events we define. By default this is true, but this can be set to
1262          * false if you need the default behavior (not recommended)
1263          * @property preventDefault
1264          * @type boolean
1265          * @static
1266          */
1267         preventDefault: true,
1268
1269         /**
1270          * Flag to determine if we should stop the propagation of the events
1271          * we generate. This is true by default but you may want to set it to
1272          * false if the html element contains other features that require the
1273          * mouse click.
1274          * @property stopPropagation
1275          * @type boolean
1276          * @static
1277          */
1278         stopPropagation: true,
1279
1280         /**
1281          * Internal flag that is set to true when drag and drop has been
1282          * intialized
1283          * @property initialized
1284          * @private
1285          * @static
1286          */
1287         initalized: false,
1288
1289         /**
1290          * All drag and drop can be disabled.
1291          * @property locked
1292          * @private
1293          * @static
1294          */
1295         locked: false,
1296
1297         /**
1298          * Called the first time an element is registered.
1299          * @method init
1300          * @private
1301          * @static
1302          */
1303         init: function() {
1304             this.initialized = true;
1305         },
1306
1307         /**
1308          * In point mode, drag and drop interaction is defined by the
1309          * location of the cursor during the drag/drop
1310          * @property POINT
1311          * @type int
1312          * @static
1313          */
1314         POINT: 0,
1315
1316         /**
1317          * In intersect mode, drag and drop interactio nis defined by the
1318          * overlap of two or more drag and drop objects.
1319          * @property INTERSECT
1320          * @type int
1321          * @static
1322          */
1323         INTERSECT: 1,
1324
1325         /**
1326          * The current drag and drop mode.  Default: POINT
1327          * @property mode
1328          * @type int
1329          * @static
1330          */
1331         mode: 0,
1332
1333         /**
1334          * Runs method on all drag and drop objects
1335          * @method _execOnAll
1336          * @private
1337          * @static
1338          */
1339         _execOnAll: function(sMethod, args) {
1340             for (var i in this.ids) {
1341                 for (var j in this.ids[i]) {
1342                     var oDD = this.ids[i][j];
1343                     if (! this.isTypeOfDD(oDD)) {
1344                         continue;
1345                     }
1346                     oDD[sMethod].apply(oDD, args);
1347                 }
1348             }
1349         },
1350
1351         /**
1352          * Drag and drop initialization.  Sets up the global event handlers
1353          * @method _onLoad
1354          * @private
1355          * @static
1356          */
1357         _onLoad: function() {
1358
1359             this.init();
1360
1361
1362             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1363             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1364             Event.on(window,   "unload",    this._onUnload, this, true);
1365             Event.on(window,   "resize",    this._onResize, this, true);
1366             // Event.on(window,   "mouseout",    this._test);
1367
1368         },
1369
1370         /**
1371          * Reset constraints on all drag and drop objs
1372          * @method _onResize
1373          * @private
1374          * @static
1375          */
1376         _onResize: function(e) {
1377             this._execOnAll("resetConstraints", []);
1378         },
1379
1380         /**
1381          * Lock all drag and drop functionality
1382          * @method lock
1383          * @static
1384          */
1385         lock: function() { this.locked = true; },
1386
1387         /**
1388          * Unlock all drag and drop functionality
1389          * @method unlock
1390          * @static
1391          */
1392         unlock: function() { this.locked = false; },
1393
1394         /**
1395          * Is drag and drop locked?
1396          * @method isLocked
1397          * @return {boolean} True if drag and drop is locked, false otherwise.
1398          * @static
1399          */
1400         isLocked: function() { return this.locked; },
1401
1402         /**
1403          * Location cache that is set for all drag drop objects when a drag is
1404          * initiated, cleared when the drag is finished.
1405          * @property locationCache
1406          * @private
1407          * @static
1408          */
1409         locationCache: {},
1410
1411         /**
1412          * Set useCache to false if you want to force object the lookup of each
1413          * drag and drop linked element constantly during a drag.
1414          * @property useCache
1415          * @type boolean
1416          * @static
1417          */
1418         useCache: true,
1419
1420         /**
1421          * The number of pixels that the mouse needs to move after the
1422          * mousedown before the drag is initiated.  Default=3;
1423          * @property clickPixelThresh
1424          * @type int
1425          * @static
1426          */
1427         clickPixelThresh: 3,
1428
1429         /**
1430          * The number of milliseconds after the mousedown event to initiate the
1431          * drag if we don't get a mouseup event. Default=1000
1432          * @property clickTimeThresh
1433          * @type int
1434          * @static
1435          */
1436         clickTimeThresh: 350,
1437
1438         /**
1439          * Flag that indicates that either the drag pixel threshold or the
1440          * mousdown time threshold has been met
1441          * @property dragThreshMet
1442          * @type boolean
1443          * @private
1444          * @static
1445          */
1446         dragThreshMet: false,
1447
1448         /**
1449          * Timeout used for the click time threshold
1450          * @property clickTimeout
1451          * @type Object
1452          * @private
1453          * @static
1454          */
1455         clickTimeout: null,
1456
1457         /**
1458          * The X position of the mousedown event stored for later use when a
1459          * drag threshold is met.
1460          * @property startX
1461          * @type int
1462          * @private
1463          * @static
1464          */
1465         startX: 0,
1466
1467         /**
1468          * The Y position of the mousedown event stored for later use when a
1469          * drag threshold is met.
1470          * @property startY
1471          * @type int
1472          * @private
1473          * @static
1474          */
1475         startY: 0,
1476
1477         /**
1478          * Each DragDrop instance must be registered with the DragDropMgr.
1479          * This is executed in DragDrop.init()
1480          * @method regDragDrop
1481          * @param {DragDrop} oDD the DragDrop object to register
1482          * @param {String} sGroup the name of the group this element belongs to
1483          * @static
1484          */
1485         regDragDrop: function(oDD, sGroup) {
1486             if (!this.initialized) { this.init(); }
1487
1488             if (!this.ids[sGroup]) {
1489                 this.ids[sGroup] = {};
1490             }
1491             this.ids[sGroup][oDD.id] = oDD;
1492         },
1493
1494         /**
1495          * Removes the supplied dd instance from the supplied group. Executed
1496          * by DragDrop.removeFromGroup, so don't call this function directly.
1497          * @method removeDDFromGroup
1498          * @private
1499          * @static
1500          */
1501         removeDDFromGroup: function(oDD, sGroup) {
1502             if (!this.ids[sGroup]) {
1503                 this.ids[sGroup] = {};
1504             }
1505
1506             var obj = this.ids[sGroup];
1507             if (obj && obj[oDD.id]) {
1508                 delete obj[oDD.id];
1509             }
1510         },
1511
1512         /**
1513          * Unregisters a drag and drop item.  This is executed in
1514          * DragDrop.unreg, use that method instead of calling this directly.
1515          * @method _remove
1516          * @private
1517          * @static
1518          */
1519         _remove: function(oDD) {
1520             for (var g in oDD.groups) {
1521                 if (g && this.ids[g][oDD.id]) {
1522                     delete this.ids[g][oDD.id];
1523                 }
1524             }
1525             delete this.handleIds[oDD.id];
1526         },
1527
1528         /**
1529          * Each DragDrop handle element must be registered.  This is done
1530          * automatically when executing DragDrop.setHandleElId()
1531          * @method regHandle
1532          * @param {String} sDDId the DragDrop id this element is a handle for
1533          * @param {String} sHandleId the id of the element that is the drag
1534          * handle
1535          * @static
1536          */
1537         regHandle: function(sDDId, sHandleId) {
1538             if (!this.handleIds[sDDId]) {
1539                 this.handleIds[sDDId] = {};
1540             }
1541             this.handleIds[sDDId][sHandleId] = sHandleId;
1542         },
1543
1544         /**
1545          * Utility function to determine if a given element has been
1546          * registered as a drag drop item.
1547          * @method isDragDrop
1548          * @param {String} id the element id to check
1549          * @return {boolean} true if this element is a DragDrop item,
1550          * false otherwise
1551          * @static
1552          */
1553         isDragDrop: function(id) {
1554             return ( this.getDDById(id) ) ? true : false;
1555         },
1556
1557         /**
1558          * Returns the drag and drop instances that are in all groups the
1559          * passed in instance belongs to.
1560          * @method getRelated
1561          * @param {DragDrop} p_oDD the obj to get related data for
1562          * @param {boolean} bTargetsOnly if true, only return targetable objs
1563          * @return {DragDrop[]} the related instances
1564          * @static
1565          */
1566         getRelated: function(p_oDD, bTargetsOnly) {
1567             var oDDs = [];
1568             for (var i in p_oDD.groups) {
1569                 for (j in this.ids[i]) {
1570                     var dd = this.ids[i][j];
1571                     if (! this.isTypeOfDD(dd)) {
1572                         continue;
1573                     }
1574                     if (!bTargetsOnly || dd.isTarget) {
1575                         oDDs[oDDs.length] = dd;
1576                     }
1577                 }
1578             }
1579
1580             return oDDs;
1581         },
1582
1583         /**
1584          * Returns true if the specified dd target is a legal target for
1585          * the specifice drag obj
1586          * @method isLegalTarget
1587          * @param {DragDrop} the drag obj
1588          * @param {DragDrop} the target
1589          * @return {boolean} true if the target is a legal target for the
1590          * dd obj
1591          * @static
1592          */
1593         isLegalTarget: function (oDD, oTargetDD) {
1594             var targets = this.getRelated(oDD, true);
1595             for (var i=0, len=targets.length;i<len;++i) {
1596                 if (targets[i].id == oTargetDD.id) {
1597                     return true;
1598                 }
1599             }
1600
1601             return false;
1602         },
1603
1604         /**
1605          * My goal is to be able to transparently determine if an object is
1606          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1607          * returns "object", oDD.constructor.toString() always returns
1608          * "DragDrop" and not the name of the subclass.  So for now it just
1609          * evaluates a well-known variable in DragDrop.
1610          * @method isTypeOfDD
1611          * @param {Object} the object to evaluate
1612          * @return {boolean} true if typeof oDD = DragDrop
1613          * @static
1614          */
1615         isTypeOfDD: function (oDD) {
1616             return (oDD && oDD.__ygDragDrop);
1617         },
1618
1619         /**
1620          * Utility function to determine if a given element has been
1621          * registered as a drag drop handle for the given Drag Drop object.
1622          * @method isHandle
1623          * @param {String} id the element id to check
1624          * @return {boolean} true if this element is a DragDrop handle, false
1625          * otherwise
1626          * @static
1627          */
1628         isHandle: function(sDDId, sHandleId) {
1629             return ( this.handleIds[sDDId] &&
1630                             this.handleIds[sDDId][sHandleId] );
1631         },
1632
1633         /**
1634          * Returns the DragDrop instance for a given id
1635          * @method getDDById
1636          * @param {String} id the id of the DragDrop object
1637          * @return {DragDrop} the drag drop object, null if it is not found
1638          * @static
1639          */
1640         getDDById: function(id) {
1641             for (var i in this.ids) {
1642                 if (this.ids[i][id]) {
1643                     return this.ids[i][id];
1644                 }
1645             }
1646             return null;
1647         },
1648
1649         /**
1650          * Fired after a registered DragDrop object gets the mousedown event.
1651          * Sets up the events required to track the object being dragged
1652          * @method handleMouseDown
1653          * @param {Event} e the event
1654          * @param oDD the DragDrop object being dragged
1655          * @private
1656          * @static
1657          */
1658         handleMouseDown: function(e, oDD) {
1659             if(Roo.QuickTips){
1660                 Roo.QuickTips.disable();
1661             }
1662             this.currentTarget = e.getTarget();
1663
1664             this.dragCurrent = oDD;
1665
1666             var el = oDD.getEl();
1667
1668             // track start position
1669             this.startX = e.getPageX();
1670             this.startY = e.getPageY();
1671
1672             this.deltaX = this.startX - el.offsetLeft;
1673             this.deltaY = this.startY - el.offsetTop;
1674
1675             this.dragThreshMet = false;
1676
1677             this.clickTimeout = setTimeout(
1678                     function() {
1679                         var DDM = Roo.dd.DDM;
1680                         DDM.startDrag(DDM.startX, DDM.startY);
1681                     },
1682                     this.clickTimeThresh );
1683         },
1684
1685         /**
1686          * Fired when either the drag pixel threshol or the mousedown hold
1687          * time threshold has been met.
1688          * @method startDrag
1689          * @param x {int} the X position of the original mousedown
1690          * @param y {int} the Y position of the original mousedown
1691          * @static
1692          */
1693         startDrag: function(x, y) {
1694             clearTimeout(this.clickTimeout);
1695             if (this.dragCurrent) {
1696                 this.dragCurrent.b4StartDrag(x, y);
1697                 this.dragCurrent.startDrag(x, y);
1698             }
1699             this.dragThreshMet = true;
1700         },
1701
1702         /**
1703          * Internal function to handle the mouseup event.  Will be invoked
1704          * from the context of the document.
1705          * @method handleMouseUp
1706          * @param {Event} e the event
1707          * @private
1708          * @static
1709          */
1710         handleMouseUp: function(e) {
1711
1712             if(Roo.QuickTips){
1713                 Roo.QuickTips.enable();
1714             }
1715             if (! this.dragCurrent) {
1716                 return;
1717             }
1718
1719             clearTimeout(this.clickTimeout);
1720
1721             if (this.dragThreshMet) {
1722                 this.fireEvents(e, true);
1723             } else {
1724             }
1725
1726             this.stopDrag(e);
1727
1728             this.stopEvent(e);
1729         },
1730
1731         /**
1732          * Utility to stop event propagation and event default, if these
1733          * features are turned on.
1734          * @method stopEvent
1735          * @param {Event} e the event as returned by this.getEvent()
1736          * @static
1737          */
1738         stopEvent: function(e){
1739             if(this.stopPropagation) {
1740                 e.stopPropagation();
1741             }
1742
1743             if (this.preventDefault) {
1744                 e.preventDefault();
1745             }
1746         },
1747
1748         /**
1749          * Internal function to clean up event handlers after the drag
1750          * operation is complete
1751          * @method stopDrag
1752          * @param {Event} e the event
1753          * @private
1754          * @static
1755          */
1756         stopDrag: function(e) {
1757             // Fire the drag end event for the item that was dragged
1758             if (this.dragCurrent) {
1759                 if (this.dragThreshMet) {
1760                     this.dragCurrent.b4EndDrag(e);
1761                     this.dragCurrent.endDrag(e);
1762                 }
1763
1764                 this.dragCurrent.onMouseUp(e);
1765             }
1766
1767             this.dragCurrent = null;
1768             this.dragOvers = {};
1769         },
1770
1771         /**
1772          * Internal function to handle the mousemove event.  Will be invoked
1773          * from the context of the html element.
1774          *
1775          * @TODO figure out what we can do about mouse events lost when the
1776          * user drags objects beyond the window boundary.  Currently we can
1777          * detect this in internet explorer by verifying that the mouse is
1778          * down during the mousemove event.  Firefox doesn't give us the
1779          * button state on the mousemove event.
1780          * @method handleMouseMove
1781          * @param {Event} e the event
1782          * @private
1783          * @static
1784          */
1785         handleMouseMove: function(e) {
1786             if (! this.dragCurrent) {
1787                 return true;
1788             }
1789
1790             // var button = e.which || e.button;
1791
1792             // check for IE mouseup outside of page boundary
1793             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1794                 this.stopEvent(e);
1795                 return this.handleMouseUp(e);
1796             }
1797
1798             if (!this.dragThreshMet) {
1799                 var diffX = Math.abs(this.startX - e.getPageX());
1800                 var diffY = Math.abs(this.startY - e.getPageY());
1801                 if (diffX > this.clickPixelThresh ||
1802                             diffY > this.clickPixelThresh) {
1803                     this.startDrag(this.startX, this.startY);
1804                 }
1805             }
1806
1807             if (this.dragThreshMet) {
1808                 this.dragCurrent.b4Drag(e);
1809                 this.dragCurrent.onDrag(e);
1810                 if(!this.dragCurrent.moveOnly){
1811                     this.fireEvents(e, false);
1812                 }
1813             }
1814
1815             this.stopEvent(e);
1816
1817             return true;
1818         },
1819
1820         /**
1821          * Iterates over all of the DragDrop elements to find ones we are
1822          * hovering over or dropping on
1823          * @method fireEvents
1824          * @param {Event} e the event
1825          * @param {boolean} isDrop is this a drop op or a mouseover op?
1826          * @private
1827          * @static
1828          */
1829         fireEvents: function(e, isDrop) {
1830             var dc = this.dragCurrent;
1831
1832             // If the user did the mouse up outside of the window, we could
1833             // get here even though we have ended the drag.
1834             if (!dc || dc.isLocked()) {
1835                 return;
1836             }
1837
1838             var pt = e.getPoint();
1839
1840             // cache the previous dragOver array
1841             var oldOvers = [];
1842
1843             var outEvts   = [];
1844             var overEvts  = [];
1845             var dropEvts  = [];
1846             var enterEvts = [];
1847
1848             // Check to see if the object(s) we were hovering over is no longer
1849             // being hovered over so we can fire the onDragOut event
1850             for (var i in this.dragOvers) {
1851
1852                 var ddo = this.dragOvers[i];
1853
1854                 if (! this.isTypeOfDD(ddo)) {
1855                     continue;
1856                 }
1857
1858                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1859                     outEvts.push( ddo );
1860                 }
1861
1862                 oldOvers[i] = true;
1863                 delete this.dragOvers[i];
1864             }
1865
1866             for (var sGroup in dc.groups) {
1867
1868                 if ("string" != typeof sGroup) {
1869                     continue;
1870                 }
1871
1872                 for (i in this.ids[sGroup]) {
1873                     var oDD = this.ids[sGroup][i];
1874                     if (! this.isTypeOfDD(oDD)) {
1875                         continue;
1876                     }
1877
1878                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1879                         if (this.isOverTarget(pt, oDD, this.mode)) {
1880                             // look for drop interactions
1881                             if (isDrop) {
1882                                 dropEvts.push( oDD );
1883                             // look for drag enter and drag over interactions
1884                             } else {
1885
1886                                 // initial drag over: dragEnter fires
1887                                 if (!oldOvers[oDD.id]) {
1888                                     enterEvts.push( oDD );
1889                                 // subsequent drag overs: dragOver fires
1890                                 } else {
1891                                     overEvts.push( oDD );
1892                                 }
1893
1894                                 this.dragOvers[oDD.id] = oDD;
1895                             }
1896                         }
1897                     }
1898                 }
1899             }
1900
1901             if (this.mode) {
1902                 if (outEvts.length) {
1903                     dc.b4DragOut(e, outEvts);
1904                     dc.onDragOut(e, outEvts);
1905                 }
1906
1907                 if (enterEvts.length) {
1908                     dc.onDragEnter(e, enterEvts);
1909                 }
1910
1911                 if (overEvts.length) {
1912                     dc.b4DragOver(e, overEvts);
1913                     dc.onDragOver(e, overEvts);
1914                 }
1915
1916                 if (dropEvts.length) {
1917                     dc.b4DragDrop(e, dropEvts);
1918                     dc.onDragDrop(e, dropEvts);
1919                 }
1920
1921             } else {
1922                 // fire dragout events
1923                 var len = 0;
1924                 for (i=0, len=outEvts.length; i<len; ++i) {
1925                     dc.b4DragOut(e, outEvts[i].id);
1926                     dc.onDragOut(e, outEvts[i].id);
1927                 }
1928
1929                 // fire enter events
1930                 for (i=0,len=enterEvts.length; i<len; ++i) {
1931                     // dc.b4DragEnter(e, oDD.id);
1932                     dc.onDragEnter(e, enterEvts[i].id);
1933                 }
1934
1935                 // fire over events
1936                 for (i=0,len=overEvts.length; i<len; ++i) {
1937                     dc.b4DragOver(e, overEvts[i].id);
1938                     dc.onDragOver(e, overEvts[i].id);
1939                 }
1940
1941                 // fire drop events
1942                 for (i=0, len=dropEvts.length; i<len; ++i) {
1943                     dc.b4DragDrop(e, dropEvts[i].id);
1944                     dc.onDragDrop(e, dropEvts[i].id);
1945                 }
1946
1947             }
1948
1949             // notify about a drop that did not find a target
1950             if (isDrop && !dropEvts.length) {
1951                 dc.onInvalidDrop(e);
1952             }
1953
1954         },
1955
1956         /**
1957          * Helper function for getting the best match from the list of drag
1958          * and drop objects returned by the drag and drop events when we are
1959          * in INTERSECT mode.  It returns either the first object that the
1960          * cursor is over, or the object that has the greatest overlap with
1961          * the dragged element.
1962          * @method getBestMatch
1963          * @param  {DragDrop[]} dds The array of drag and drop objects
1964          * targeted
1965          * @return {DragDrop}       The best single match
1966          * @static
1967          */
1968         getBestMatch: function(dds) {
1969             var winner = null;
1970             // Return null if the input is not what we expect
1971             //if (!dds || !dds.length || dds.length == 0) {
1972                // winner = null;
1973             // If there is only one item, it wins
1974             //} else if (dds.length == 1) {
1975
1976             var len = dds.length;
1977
1978             if (len == 1) {
1979                 winner = dds[0];
1980             } else {
1981                 // Loop through the targeted items
1982                 for (var i=0; i<len; ++i) {
1983                     var dd = dds[i];
1984                     // If the cursor is over the object, it wins.  If the
1985                     // cursor is over multiple matches, the first one we come
1986                     // to wins.
1987                     if (dd.cursorIsOver) {
1988                         winner = dd;
1989                         break;
1990                     // Otherwise the object with the most overlap wins
1991                     } else {
1992                         if (!winner ||
1993                             winner.overlap.getArea() < dd.overlap.getArea()) {
1994                             winner = dd;
1995                         }
1996                     }
1997                 }
1998             }
1999
2000             return winner;
2001         },
2002
2003         /**
2004          * Refreshes the cache of the top-left and bottom-right points of the
2005          * drag and drop objects in the specified group(s).  This is in the
2006          * format that is stored in the drag and drop instance, so typical
2007          * usage is:
2008          * <code>
2009          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2010          * </code>
2011          * Alternatively:
2012          * <code>
2013          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2014          * </code>
2015          * @TODO this really should be an indexed array.  Alternatively this
2016          * method could accept both.
2017          * @method refreshCache
2018          * @param {Object} groups an associative array of groups to refresh
2019          * @static
2020          */
2021         refreshCache: function(groups) {
2022             for (var sGroup in groups) {
2023                 if ("string" != typeof sGroup) {
2024                     continue;
2025                 }
2026                 for (var i in this.ids[sGroup]) {
2027                     var oDD = this.ids[sGroup][i];
2028
2029                     if (this.isTypeOfDD(oDD)) {
2030                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2031                         var loc = this.getLocation(oDD);
2032                         if (loc) {
2033                             this.locationCache[oDD.id] = loc;
2034                         } else {
2035                             delete this.locationCache[oDD.id];
2036                             // this will unregister the drag and drop object if
2037                             // the element is not in a usable state
2038                             // oDD.unreg();
2039                         }
2040                     }
2041                 }
2042             }
2043         },
2044
2045         /**
2046          * This checks to make sure an element exists and is in the DOM.  The
2047          * main purpose is to handle cases where innerHTML is used to remove
2048          * drag and drop objects from the DOM.  IE provides an 'unspecified
2049          * error' when trying to access the offsetParent of such an element
2050          * @method verifyEl
2051          * @param {HTMLElement} el the element to check
2052          * @return {boolean} true if the element looks usable
2053          * @static
2054          */
2055         verifyEl: function(el) {
2056             if (el) {
2057                 var parent;
2058                 if(Roo.isIE){
2059                     try{
2060                         parent = el.offsetParent;
2061                     }catch(e){}
2062                 }else{
2063                     parent = el.offsetParent;
2064                 }
2065                 if (parent) {
2066                     return true;
2067                 }
2068             }
2069
2070             return false;
2071         },
2072
2073         /**
2074          * Returns a Region object containing the drag and drop element's position
2075          * and size, including the padding configured for it
2076          * @method getLocation
2077          * @param {DragDrop} oDD the drag and drop object to get the
2078          *                       location for
2079          * @return {Roo.lib.Region} a Region object representing the total area
2080          *                             the element occupies, including any padding
2081          *                             the instance is configured for.
2082          * @static
2083          */
2084         getLocation: function(oDD) {
2085             if (! this.isTypeOfDD(oDD)) {
2086                 return null;
2087             }
2088
2089             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2090
2091             try {
2092                 pos= Roo.lib.Dom.getXY(el);
2093             } catch (e) { }
2094
2095             if (!pos) {
2096                 return null;
2097             }
2098
2099             x1 = pos[0];
2100             x2 = x1 + el.offsetWidth;
2101             y1 = pos[1];
2102             y2 = y1 + el.offsetHeight;
2103
2104             t = y1 - oDD.padding[0];
2105             r = x2 + oDD.padding[1];
2106             b = y2 + oDD.padding[2];
2107             l = x1 - oDD.padding[3];
2108
2109             return new Roo.lib.Region( t, r, b, l );
2110         },
2111
2112         /**
2113          * Checks the cursor location to see if it over the target
2114          * @method isOverTarget
2115          * @param {Roo.lib.Point} pt The point to evaluate
2116          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2117          * @return {boolean} true if the mouse is over the target
2118          * @private
2119          * @static
2120          */
2121         isOverTarget: function(pt, oTarget, intersect) {
2122             // use cache if available
2123             var loc = this.locationCache[oTarget.id];
2124             if (!loc || !this.useCache) {
2125                 loc = this.getLocation(oTarget);
2126                 this.locationCache[oTarget.id] = loc;
2127
2128             }
2129
2130             if (!loc) {
2131                 return false;
2132             }
2133
2134             oTarget.cursorIsOver = loc.contains( pt );
2135
2136             // DragDrop is using this as a sanity check for the initial mousedown
2137             // in this case we are done.  In POINT mode, if the drag obj has no
2138             // contraints, we are also done. Otherwise we need to evaluate the
2139             // location of the target as related to the actual location of the
2140             // dragged element.
2141             var dc = this.dragCurrent;
2142             if (!dc || !dc.getTargetCoord ||
2143                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2144                 return oTarget.cursorIsOver;
2145             }
2146
2147             oTarget.overlap = null;
2148
2149             // Get the current location of the drag element, this is the
2150             // location of the mouse event less the delta that represents
2151             // where the original mousedown happened on the element.  We
2152             // need to consider constraints and ticks as well.
2153             var pos = dc.getTargetCoord(pt.x, pt.y);
2154
2155             var el = dc.getDragEl();
2156             var curRegion = new Roo.lib.Region( pos.y,
2157                                                    pos.x + el.offsetWidth,
2158                                                    pos.y + el.offsetHeight,
2159                                                    pos.x );
2160
2161             var overlap = curRegion.intersect(loc);
2162
2163             if (overlap) {
2164                 oTarget.overlap = overlap;
2165                 return (intersect) ? true : oTarget.cursorIsOver;
2166             } else {
2167                 return false;
2168             }
2169         },
2170
2171         /**
2172          * unload event handler
2173          * @method _onUnload
2174          * @private
2175          * @static
2176          */
2177         _onUnload: function(e, me) {
2178             Roo.dd.DragDropMgr.unregAll();
2179         },
2180
2181         /**
2182          * Cleans up the drag and drop events and objects.
2183          * @method unregAll
2184          * @private
2185          * @static
2186          */
2187         unregAll: function() {
2188
2189             if (this.dragCurrent) {
2190                 this.stopDrag();
2191                 this.dragCurrent = null;
2192             }
2193
2194             this._execOnAll("unreg", []);
2195
2196             for (i in this.elementCache) {
2197                 delete this.elementCache[i];
2198             }
2199
2200             this.elementCache = {};
2201             this.ids = {};
2202         },
2203
2204         /**
2205          * A cache of DOM elements
2206          * @property elementCache
2207          * @private
2208          * @static
2209          */
2210         elementCache: {},
2211
2212         /**
2213          * Get the wrapper for the DOM element specified
2214          * @method getElWrapper
2215          * @param {String} id the id of the element to get
2216          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2217          * @private
2218          * @deprecated This wrapper isn't that useful
2219          * @static
2220          */
2221         getElWrapper: function(id) {
2222             var oWrapper = this.elementCache[id];
2223             if (!oWrapper || !oWrapper.el) {
2224                 oWrapper = this.elementCache[id] =
2225                     new this.ElementWrapper(Roo.getDom(id));
2226             }
2227             return oWrapper;
2228         },
2229
2230         /**
2231          * Returns the actual DOM element
2232          * @method getElement
2233          * @param {String} id the id of the elment to get
2234          * @return {Object} The element
2235          * @deprecated use Roo.getDom instead
2236          * @static
2237          */
2238         getElement: function(id) {
2239             return Roo.getDom(id);
2240         },
2241
2242         /**
2243          * Returns the style property for the DOM element (i.e.,
2244          * document.getElById(id).style)
2245          * @method getCss
2246          * @param {String} id the id of the elment to get
2247          * @return {Object} The style property of the element
2248          * @deprecated use Roo.getDom instead
2249          * @static
2250          */
2251         getCss: function(id) {
2252             var el = Roo.getDom(id);
2253             return (el) ? el.style : null;
2254         },
2255
2256         /**
2257          * Inner class for cached elements
2258          * @class DragDropMgr.ElementWrapper
2259          * @for DragDropMgr
2260          * @private
2261          * @deprecated
2262          */
2263         ElementWrapper: function(el) {
2264                 /**
2265                  * The element
2266                  * @property el
2267                  */
2268                 this.el = el || null;
2269                 /**
2270                  * The element id
2271                  * @property id
2272                  */
2273                 this.id = this.el && el.id;
2274                 /**
2275                  * A reference to the style property
2276                  * @property css
2277                  */
2278                 this.css = this.el && el.style;
2279             },
2280
2281         /**
2282          * Returns the X position of an html element
2283          * @method getPosX
2284          * @param el the element for which to get the position
2285          * @return {int} the X coordinate
2286          * @for DragDropMgr
2287          * @deprecated use Roo.lib.Dom.getX instead
2288          * @static
2289          */
2290         getPosX: function(el) {
2291             return Roo.lib.Dom.getX(el);
2292         },
2293
2294         /**
2295          * Returns the Y position of an html element
2296          * @method getPosY
2297          * @param el the element for which to get the position
2298          * @return {int} the Y coordinate
2299          * @deprecated use Roo.lib.Dom.getY instead
2300          * @static
2301          */
2302         getPosY: function(el) {
2303             return Roo.lib.Dom.getY(el);
2304         },
2305
2306         /**
2307          * Swap two nodes.  In IE, we use the native method, for others we
2308          * emulate the IE behavior
2309          * @method swapNode
2310          * @param n1 the first node to swap
2311          * @param n2 the other node to swap
2312          * @static
2313          */
2314         swapNode: function(n1, n2) {
2315             if (n1.swapNode) {
2316                 n1.swapNode(n2);
2317             } else {
2318                 var p = n2.parentNode;
2319                 var s = n2.nextSibling;
2320
2321                 if (s == n1) {
2322                     p.insertBefore(n1, n2);
2323                 } else if (n2 == n1.nextSibling) {
2324                     p.insertBefore(n2, n1);
2325                 } else {
2326                     n1.parentNode.replaceChild(n2, n1);
2327                     p.insertBefore(n1, s);
2328                 }
2329             }
2330         },
2331
2332         /**
2333          * Returns the current scroll position
2334          * @method getScroll
2335          * @private
2336          * @static
2337          */
2338         getScroll: function () {
2339             var t, l, dde=document.documentElement, db=document.body;
2340             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2341                 t = dde.scrollTop;
2342                 l = dde.scrollLeft;
2343             } else if (db) {
2344                 t = db.scrollTop;
2345                 l = db.scrollLeft;
2346             } else {
2347
2348             }
2349             return { top: t, left: l };
2350         },
2351
2352         /**
2353          * Returns the specified element style property
2354          * @method getStyle
2355          * @param {HTMLElement} el          the element
2356          * @param {string}      styleProp   the style property
2357          * @return {string} The value of the style property
2358          * @deprecated use Roo.lib.Dom.getStyle
2359          * @static
2360          */
2361         getStyle: function(el, styleProp) {
2362             return Roo.fly(el).getStyle(styleProp);
2363         },
2364
2365         /**
2366          * Gets the scrollTop
2367          * @method getScrollTop
2368          * @return {int} the document's scrollTop
2369          * @static
2370          */
2371         getScrollTop: function () { return this.getScroll().top; },
2372
2373         /**
2374          * Gets the scrollLeft
2375          * @method getScrollLeft
2376          * @return {int} the document's scrollTop
2377          * @static
2378          */
2379         getScrollLeft: function () { return this.getScroll().left; },
2380
2381         /**
2382          * Sets the x/y position of an element to the location of the
2383          * target element.
2384          * @method moveToEl
2385          * @param {HTMLElement} moveEl      The element to move
2386          * @param {HTMLElement} targetEl    The position reference element
2387          * @static
2388          */
2389         moveToEl: function (moveEl, targetEl) {
2390             var aCoord = Roo.lib.Dom.getXY(targetEl);
2391             Roo.lib.Dom.setXY(moveEl, aCoord);
2392         },
2393
2394         /**
2395          * Numeric array sort function
2396          * @method numericSort
2397          * @static
2398          */
2399         numericSort: function(a, b) { return (a - b); },
2400
2401         /**
2402          * Internal counter
2403          * @property _timeoutCount
2404          * @private
2405          * @static
2406          */
2407         _timeoutCount: 0,
2408
2409         /**
2410          * Trying to make the load order less important.  Without this we get
2411          * an error if this file is loaded before the Event Utility.
2412          * @method _addListeners
2413          * @private
2414          * @static
2415          */
2416         _addListeners: function() {
2417             var DDM = Roo.dd.DDM;
2418             if ( Roo.lib.Event && document ) {
2419                 DDM._onLoad();
2420             } else {
2421                 if (DDM._timeoutCount > 2000) {
2422                 } else {
2423                     setTimeout(DDM._addListeners, 10);
2424                     if (document && document.body) {
2425                         DDM._timeoutCount += 1;
2426                     }
2427                 }
2428             }
2429         },
2430
2431         /**
2432          * Recursively searches the immediate parent and all child nodes for
2433          * the handle element in order to determine wheter or not it was
2434          * clicked.
2435          * @method handleWasClicked
2436          * @param node the html element to inspect
2437          * @static
2438          */
2439         handleWasClicked: function(node, id) {
2440             if (this.isHandle(id, node.id)) {
2441                 return true;
2442             } else {
2443                 // check to see if this is a text node child of the one we want
2444                 var p = node.parentNode;
2445
2446                 while (p) {
2447                     if (this.isHandle(id, p.id)) {
2448                         return true;
2449                     } else {
2450                         p = p.parentNode;
2451                     }
2452                 }
2453             }
2454
2455             return false;
2456         }
2457
2458     };
2459
2460 }();
2461
2462 // shorter alias, save a few bytes
2463 Roo.dd.DDM = Roo.dd.DragDropMgr;
2464 Roo.dd.DDM._addListeners();
2465
2466 }/*
2467  * Based on:
2468  * Ext JS Library 1.1.1
2469  * Copyright(c) 2006-2007, Ext JS, LLC.
2470  *
2471  * Originally Released Under LGPL - original licence link has changed is not relivant.
2472  *
2473  * Fork - LGPL
2474  * <script type="text/javascript">
2475  */
2476
2477 /**
2478  * @class Roo.dd.DD
2479  * A DragDrop implementation where the linked element follows the
2480  * mouse cursor during a drag.
2481  * @extends Roo.dd.DragDrop
2482  * @constructor
2483  * @param {String} id the id of the linked element
2484  * @param {String} sGroup the group of related DragDrop items
2485  * @param {object} config an object containing configurable attributes
2486  *                Valid properties for DD:
2487  *                    scroll
2488  */
2489 Roo.dd.DD = function(id, sGroup, config) {
2490     if (id) {
2491         this.init(id, sGroup, config);
2492     }
2493 };
2494
2495 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2496
2497     /**
2498      * When set to true, the utility automatically tries to scroll the browser
2499      * window wehn a drag and drop element is dragged near the viewport boundary.
2500      * Defaults to true.
2501      * @property scroll
2502      * @type boolean
2503      */
2504     scroll: true,
2505
2506     /**
2507      * Sets the pointer offset to the distance between the linked element's top
2508      * left corner and the location the element was clicked
2509      * @method autoOffset
2510      * @param {int} iPageX the X coordinate of the click
2511      * @param {int} iPageY the Y coordinate of the click
2512      */
2513     autoOffset: function(iPageX, iPageY) {
2514         var x = iPageX - this.startPageX;
2515         var y = iPageY - this.startPageY;
2516         this.setDelta(x, y);
2517     },
2518
2519     /**
2520      * Sets the pointer offset.  You can call this directly to force the
2521      * offset to be in a particular location (e.g., pass in 0,0 to set it
2522      * to the center of the object)
2523      * @method setDelta
2524      * @param {int} iDeltaX the distance from the left
2525      * @param {int} iDeltaY the distance from the top
2526      */
2527     setDelta: function(iDeltaX, iDeltaY) {
2528         this.deltaX = iDeltaX;
2529         this.deltaY = iDeltaY;
2530     },
2531
2532     /**
2533      * Sets the drag element to the location of the mousedown or click event,
2534      * maintaining the cursor location relative to the location on the element
2535      * that was clicked.  Override this if you want to place the element in a
2536      * location other than where the cursor is.
2537      * @method setDragElPos
2538      * @param {int} iPageX the X coordinate of the mousedown or drag event
2539      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2540      */
2541     setDragElPos: function(iPageX, iPageY) {
2542         // the first time we do this, we are going to check to make sure
2543         // the element has css positioning
2544
2545         var el = this.getDragEl();
2546         this.alignElWithMouse(el, iPageX, iPageY);
2547     },
2548
2549     /**
2550      * Sets the element to the location of the mousedown or click event,
2551      * maintaining the cursor location relative to the location on the element
2552      * that was clicked.  Override this if you want to place the element in a
2553      * location other than where the cursor is.
2554      * @method alignElWithMouse
2555      * @param {HTMLElement} el the element to move
2556      * @param {int} iPageX the X coordinate of the mousedown or drag event
2557      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2558      */
2559     alignElWithMouse: function(el, iPageX, iPageY) {
2560         var oCoord = this.getTargetCoord(iPageX, iPageY);
2561         var fly = el.dom ? el : Roo.fly(el);
2562         if (!this.deltaSetXY) {
2563             var aCoord = [oCoord.x, oCoord.y];
2564             fly.setXY(aCoord);
2565             var newLeft = fly.getLeft(true);
2566             var newTop  = fly.getTop(true);
2567             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2568         } else {
2569             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2570         }
2571
2572         this.cachePosition(oCoord.x, oCoord.y);
2573         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2574         return oCoord;
2575     },
2576
2577     /**
2578      * Saves the most recent position so that we can reset the constraints and
2579      * tick marks on-demand.  We need to know this so that we can calculate the
2580      * number of pixels the element is offset from its original position.
2581      * @method cachePosition
2582      * @param iPageX the current x position (optional, this just makes it so we
2583      * don't have to look it up again)
2584      * @param iPageY the current y position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      */
2587     cachePosition: function(iPageX, iPageY) {
2588         if (iPageX) {
2589             this.lastPageX = iPageX;
2590             this.lastPageY = iPageY;
2591         } else {
2592             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2593             this.lastPageX = aCoord[0];
2594             this.lastPageY = aCoord[1];
2595         }
2596     },
2597
2598     /**
2599      * Auto-scroll the window if the dragged object has been moved beyond the
2600      * visible window boundary.
2601      * @method autoScroll
2602      * @param {int} x the drag element's x position
2603      * @param {int} y the drag element's y position
2604      * @param {int} h the height of the drag element
2605      * @param {int} w the width of the drag element
2606      * @private
2607      */
2608     autoScroll: function(x, y, h, w) {
2609
2610         if (this.scroll) {
2611             // The client height
2612             var clientH = Roo.lib.Dom.getViewWidth();
2613
2614             // The client width
2615             var clientW = Roo.lib.Dom.getViewHeight();
2616
2617             // The amt scrolled down
2618             var st = this.DDM.getScrollTop();
2619
2620             // The amt scrolled right
2621             var sl = this.DDM.getScrollLeft();
2622
2623             // Location of the bottom of the element
2624             var bot = h + y;
2625
2626             // Location of the right of the element
2627             var right = w + x;
2628
2629             // The distance from the cursor to the bottom of the visible area,
2630             // adjusted so that we don't scroll if the cursor is beyond the
2631             // element drag constraints
2632             var toBot = (clientH + st - y - this.deltaY);
2633
2634             // The distance from the cursor to the right of the visible area
2635             var toRight = (clientW + sl - x - this.deltaX);
2636
2637
2638             // How close to the edge the cursor must be before we scroll
2639             // var thresh = (document.all) ? 100 : 40;
2640             var thresh = 40;
2641
2642             // How many pixels to scroll per autoscroll op.  This helps to reduce
2643             // clunky scrolling. IE is more sensitive about this ... it needs this
2644             // value to be higher.
2645             var scrAmt = (document.all) ? 80 : 30;
2646
2647             // Scroll down if we are near the bottom of the visible page and the
2648             // obj extends below the crease
2649             if ( bot > clientH && toBot < thresh ) {
2650                 window.scrollTo(sl, st + scrAmt);
2651             }
2652
2653             // Scroll up if the window is scrolled down and the top of the object
2654             // goes above the top border
2655             if ( y < st && st > 0 && y - st < thresh ) {
2656                 window.scrollTo(sl, st - scrAmt);
2657             }
2658
2659             // Scroll right if the obj is beyond the right border and the cursor is
2660             // near the border.
2661             if ( right > clientW && toRight < thresh ) {
2662                 window.scrollTo(sl + scrAmt, st);
2663             }
2664
2665             // Scroll left if the window has been scrolled to the right and the obj
2666             // extends past the left border
2667             if ( x < sl && sl > 0 && x - sl < thresh ) {
2668                 window.scrollTo(sl - scrAmt, st);
2669             }
2670         }
2671     },
2672
2673     /**
2674      * Finds the location the element should be placed if we want to move
2675      * it to where the mouse location less the click offset would place us.
2676      * @method getTargetCoord
2677      * @param {int} iPageX the X coordinate of the click
2678      * @param {int} iPageY the Y coordinate of the click
2679      * @return an object that contains the coordinates (Object.x and Object.y)
2680      * @private
2681      */
2682     getTargetCoord: function(iPageX, iPageY) {
2683
2684
2685         var x = iPageX - this.deltaX;
2686         var y = iPageY - this.deltaY;
2687
2688         if (this.constrainX) {
2689             if (x < this.minX) { x = this.minX; }
2690             if (x > this.maxX) { x = this.maxX; }
2691         }
2692
2693         if (this.constrainY) {
2694             if (y < this.minY) { y = this.minY; }
2695             if (y > this.maxY) { y = this.maxY; }
2696         }
2697
2698         x = this.getTick(x, this.xTicks);
2699         y = this.getTick(y, this.yTicks);
2700
2701
2702         return {x:x, y:y};
2703     },
2704
2705     /*
2706      * Sets up config options specific to this class. Overrides
2707      * Roo.dd.DragDrop, but all versions of this method through the
2708      * inheritance chain are called
2709      */
2710     applyConfig: function() {
2711         Roo.dd.DD.superclass.applyConfig.call(this);
2712         this.scroll = (this.config.scroll !== false);
2713     },
2714
2715     /*
2716      * Event that fires prior to the onMouseDown event.  Overrides
2717      * Roo.dd.DragDrop.
2718      */
2719     b4MouseDown: function(e) {
2720         // this.resetConstraints();
2721         this.autoOffset(e.getPageX(),
2722                             e.getPageY());
2723     },
2724
2725     /*
2726      * Event that fires prior to the onDrag event.  Overrides
2727      * Roo.dd.DragDrop.
2728      */
2729     b4Drag: function(e) {
2730         this.setDragElPos(e.getPageX(),
2731                             e.getPageY());
2732     },
2733
2734     toString: function() {
2735         return ("DD " + this.id);
2736     }
2737
2738     //////////////////////////////////////////////////////////////////////////
2739     // Debugging ygDragDrop events that can be overridden
2740     //////////////////////////////////////////////////////////////////////////
2741     /*
2742     startDrag: function(x, y) {
2743     },
2744
2745     onDrag: function(e) {
2746     },
2747
2748     onDragEnter: function(e, id) {
2749     },
2750
2751     onDragOver: function(e, id) {
2752     },
2753
2754     onDragOut: function(e, id) {
2755     },
2756
2757     onDragDrop: function(e, id) {
2758     },
2759
2760     endDrag: function(e) {
2761     }
2762
2763     */
2764
2765 });/*
2766  * Based on:
2767  * Ext JS Library 1.1.1
2768  * Copyright(c) 2006-2007, Ext JS, LLC.
2769  *
2770  * Originally Released Under LGPL - original licence link has changed is not relivant.
2771  *
2772  * Fork - LGPL
2773  * <script type="text/javascript">
2774  */
2775
2776 /**
2777  * @class Roo.dd.DDProxy
2778  * A DragDrop implementation that inserts an empty, bordered div into
2779  * the document that follows the cursor during drag operations.  At the time of
2780  * the click, the frame div is resized to the dimensions of the linked html
2781  * element, and moved to the exact location of the linked element.
2782  *
2783  * References to the "frame" element refer to the single proxy element that
2784  * was created to be dragged in place of all DDProxy elements on the
2785  * page.
2786  *
2787  * @extends Roo.dd.DD
2788  * @constructor
2789  * @param {String} id the id of the linked html element
2790  * @param {String} sGroup the group of related DragDrop objects
2791  * @param {object} config an object containing configurable attributes
2792  *                Valid properties for DDProxy in addition to those in DragDrop:
2793  *                   resizeFrame, centerFrame, dragElId
2794  */
2795 Roo.dd.DDProxy = function(id, sGroup, config) {
2796     if (id) {
2797         this.init(id, sGroup, config);
2798         this.initFrame();
2799     }
2800 };
2801
2802 /**
2803  * The default drag frame div id
2804  * @property Roo.dd.DDProxy.dragElId
2805  * @type String
2806  * @static
2807  */
2808 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2809
2810 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2811
2812     /**
2813      * By default we resize the drag frame to be the same size as the element
2814      * we want to drag (this is to get the frame effect).  We can turn it off
2815      * if we want a different behavior.
2816      * @property resizeFrame
2817      * @type boolean
2818      */
2819     resizeFrame: true,
2820
2821     /**
2822      * By default the frame is positioned exactly where the drag element is, so
2823      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2824      * you do not have constraints on the obj is to have the drag frame centered
2825      * around the cursor.  Set centerFrame to true for this effect.
2826      * @property centerFrame
2827      * @type boolean
2828      */
2829     centerFrame: false,
2830
2831     /**
2832      * Creates the proxy element if it does not yet exist
2833      * @method createFrame
2834      */
2835     createFrame: function() {
2836         var self = this;
2837         var body = document.body;
2838
2839         if (!body || !body.firstChild) {
2840             setTimeout( function() { self.createFrame(); }, 50 );
2841             return;
2842         }
2843
2844         var div = this.getDragEl();
2845
2846         if (!div) {
2847             div    = document.createElement("div");
2848             div.id = this.dragElId;
2849             var s  = div.style;
2850
2851             s.position   = "absolute";
2852             s.visibility = "hidden";
2853             s.cursor     = "move";
2854             s.border     = "2px solid #aaa";
2855             s.zIndex     = 999;
2856
2857             // appendChild can blow up IE if invoked prior to the window load event
2858             // while rendering a table.  It is possible there are other scenarios
2859             // that would cause this to happen as well.
2860             body.insertBefore(div, body.firstChild);
2861         }
2862     },
2863
2864     /**
2865      * Initialization for the drag frame element.  Must be called in the
2866      * constructor of all subclasses
2867      * @method initFrame
2868      */
2869     initFrame: function() {
2870         this.createFrame();
2871     },
2872
2873     applyConfig: function() {
2874         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2875
2876         this.resizeFrame = (this.config.resizeFrame !== false);
2877         this.centerFrame = (this.config.centerFrame);
2878         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2879     },
2880
2881     /**
2882      * Resizes the drag frame to the dimensions of the clicked object, positions
2883      * it over the object, and finally displays it
2884      * @method showFrame
2885      * @param {int} iPageX X click position
2886      * @param {int} iPageY Y click position
2887      * @private
2888      */
2889     showFrame: function(iPageX, iPageY) {
2890         var el = this.getEl();
2891         var dragEl = this.getDragEl();
2892         var s = dragEl.style;
2893
2894         this._resizeProxy();
2895
2896         if (this.centerFrame) {
2897             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2898                            Math.round(parseInt(s.height, 10)/2) );
2899         }
2900
2901         this.setDragElPos(iPageX, iPageY);
2902
2903         Roo.fly(dragEl).show();
2904     },
2905
2906     /**
2907      * The proxy is automatically resized to the dimensions of the linked
2908      * element when a drag is initiated, unless resizeFrame is set to false
2909      * @method _resizeProxy
2910      * @private
2911      */
2912     _resizeProxy: function() {
2913         if (this.resizeFrame) {
2914             var el = this.getEl();
2915             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2916         }
2917     },
2918
2919     // overrides Roo.dd.DragDrop
2920     b4MouseDown: function(e) {
2921         var x = e.getPageX();
2922         var y = e.getPageY();
2923         this.autoOffset(x, y);
2924         this.setDragElPos(x, y);
2925     },
2926
2927     // overrides Roo.dd.DragDrop
2928     b4StartDrag: function(x, y) {
2929         // show the drag frame
2930         this.showFrame(x, y);
2931     },
2932
2933     // overrides Roo.dd.DragDrop
2934     b4EndDrag: function(e) {
2935         Roo.fly(this.getDragEl()).hide();
2936     },
2937
2938     // overrides Roo.dd.DragDrop
2939     // By default we try to move the element to the last location of the frame.
2940     // This is so that the default behavior mirrors that of Roo.dd.DD.
2941     endDrag: function(e) {
2942
2943         var lel = this.getEl();
2944         var del = this.getDragEl();
2945
2946         // Show the drag frame briefly so we can get its position
2947         del.style.visibility = "";
2948
2949         this.beforeMove();
2950         // Hide the linked element before the move to get around a Safari
2951         // rendering bug.
2952         lel.style.visibility = "hidden";
2953         Roo.dd.DDM.moveToEl(lel, del);
2954         del.style.visibility = "hidden";
2955         lel.style.visibility = "";
2956
2957         this.afterDrag();
2958     },
2959
2960     beforeMove : function(){
2961
2962     },
2963
2964     afterDrag : function(){
2965
2966     },
2967
2968     toString: function() {
2969         return ("DDProxy " + this.id);
2970     }
2971
2972 });
2973 /*
2974  * Based on:
2975  * Ext JS Library 1.1.1
2976  * Copyright(c) 2006-2007, Ext JS, LLC.
2977  *
2978  * Originally Released Under LGPL - original licence link has changed is not relivant.
2979  *
2980  * Fork - LGPL
2981  * <script type="text/javascript">
2982  */
2983
2984  /**
2985  * @class Roo.dd.DDTarget
2986  * A DragDrop implementation that does not move, but can be a drop
2987  * target.  You would get the same result by simply omitting implementation
2988  * for the event callbacks, but this way we reduce the processing cost of the
2989  * event listener and the callbacks.
2990  * @extends Roo.dd.DragDrop
2991  * @constructor
2992  * @param {String} id the id of the element that is a drop target
2993  * @param {String} sGroup the group of related DragDrop objects
2994  * @param {object} config an object containing configurable attributes
2995  *                 Valid properties for DDTarget in addition to those in
2996  *                 DragDrop:
2997  *                    none
2998  */
2999 Roo.dd.DDTarget = function(id, sGroup, config) {
3000     if (id) {
3001         this.initTarget(id, sGroup, config);
3002     }
3003 };
3004
3005 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3006 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3007     toString: function() {
3008         return ("DDTarget " + this.id);
3009     }
3010 });
3011 /*
3012  * Based on:
3013  * Ext JS Library 1.1.1
3014  * Copyright(c) 2006-2007, Ext JS, LLC.
3015  *
3016  * Originally Released Under LGPL - original licence link has changed is not relivant.
3017  *
3018  * Fork - LGPL
3019  * <script type="text/javascript">
3020  */
3021  
3022
3023 /**
3024  * @class Roo.dd.ScrollManager
3025  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3026  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3027  * @singleton
3028  */
3029 Roo.dd.ScrollManager = function(){
3030     var ddm = Roo.dd.DragDropMgr;
3031     var els = {};
3032     var dragEl = null;
3033     var proc = {};
3034     
3035     var onStop = function(e){
3036         dragEl = null;
3037         clearProc();
3038     };
3039     
3040     var triggerRefresh = function(){
3041         if(ddm.dragCurrent){
3042              ddm.refreshCache(ddm.dragCurrent.groups);
3043         }
3044     };
3045     
3046     var doScroll = function(){
3047         if(ddm.dragCurrent){
3048             var dds = Roo.dd.ScrollManager;
3049             if(!dds.animate){
3050                 if(proc.el.scroll(proc.dir, dds.increment)){
3051                     triggerRefresh();
3052                 }
3053             }else{
3054                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3055             }
3056         }
3057     };
3058     
3059     var clearProc = function(){
3060         if(proc.id){
3061             clearInterval(proc.id);
3062         }
3063         proc.id = 0;
3064         proc.el = null;
3065         proc.dir = "";
3066     };
3067     
3068     var startProc = function(el, dir){
3069         clearProc();
3070         proc.el = el;
3071         proc.dir = dir;
3072         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3073     };
3074     
3075     var onFire = function(e, isDrop){
3076         if(isDrop || !ddm.dragCurrent){ return; }
3077         var dds = Roo.dd.ScrollManager;
3078         if(!dragEl || dragEl != ddm.dragCurrent){
3079             dragEl = ddm.dragCurrent;
3080             // refresh regions on drag start
3081             dds.refreshCache();
3082         }
3083         
3084         var xy = Roo.lib.Event.getXY(e);
3085         var pt = new Roo.lib.Point(xy[0], xy[1]);
3086         for(var id in els){
3087             var el = els[id], r = el._region;
3088             if(r && r.contains(pt) && el.isScrollable()){
3089                 if(r.bottom - pt.y <= dds.thresh){
3090                     if(proc.el != el){
3091                         startProc(el, "down");
3092                     }
3093                     return;
3094                 }else if(r.right - pt.x <= dds.thresh){
3095                     if(proc.el != el){
3096                         startProc(el, "left");
3097                     }
3098                     return;
3099                 }else if(pt.y - r.top <= dds.thresh){
3100                     if(proc.el != el){
3101                         startProc(el, "up");
3102                     }
3103                     return;
3104                 }else if(pt.x - r.left <= dds.thresh){
3105                     if(proc.el != el){
3106                         startProc(el, "right");
3107                     }
3108                     return;
3109                 }
3110             }
3111         }
3112         clearProc();
3113     };
3114     
3115     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3116     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3117     
3118     return {
3119         /**
3120          * Registers new overflow element(s) to auto scroll
3121          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3122          */
3123         register : function(el){
3124             if(el instanceof Array){
3125                 for(var i = 0, len = el.length; i < len; i++) {
3126                         this.register(el[i]);
3127                 }
3128             }else{
3129                 el = Roo.get(el);
3130                 els[el.id] = el;
3131             }
3132         },
3133         
3134         /**
3135          * Unregisters overflow element(s) so they are no longer scrolled
3136          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3137          */
3138         unregister : function(el){
3139             if(el instanceof Array){
3140                 for(var i = 0, len = el.length; i < len; i++) {
3141                         this.unregister(el[i]);
3142                 }
3143             }else{
3144                 el = Roo.get(el);
3145                 delete els[el.id];
3146             }
3147         },
3148         
3149         /**
3150          * The number of pixels from the edge of a container the pointer needs to be to 
3151          * trigger scrolling (defaults to 25)
3152          * @type Number
3153          */
3154         thresh : 25,
3155         
3156         /**
3157          * The number of pixels to scroll in each scroll increment (defaults to 50)
3158          * @type Number
3159          */
3160         increment : 100,
3161         
3162         /**
3163          * The frequency of scrolls in milliseconds (defaults to 500)
3164          * @type Number
3165          */
3166         frequency : 500,
3167         
3168         /**
3169          * True to animate the scroll (defaults to true)
3170          * @type Boolean
3171          */
3172         animate: true,
3173         
3174         /**
3175          * The animation duration in seconds - 
3176          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3177          * @type Number
3178          */
3179         animDuration: .4,
3180         
3181         /**
3182          * Manually trigger a cache refresh.
3183          */
3184         refreshCache : function(){
3185             for(var id in els){
3186                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3187                     els[id]._region = els[id].getRegion();
3188                 }
3189             }
3190         }
3191     };
3192 }();/*
3193  * Based on:
3194  * Ext JS Library 1.1.1
3195  * Copyright(c) 2006-2007, Ext JS, LLC.
3196  *
3197  * Originally Released Under LGPL - original licence link has changed is not relivant.
3198  *
3199  * Fork - LGPL
3200  * <script type="text/javascript">
3201  */
3202  
3203
3204 /**
3205  * @class Roo.dd.Registry
3206  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3207  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3208  * @singleton
3209  */
3210 Roo.dd.Registry = function(){
3211     var elements = {}; 
3212     var handles = {}; 
3213     var autoIdSeed = 0;
3214
3215     var getId = function(el, autogen){
3216         if(typeof el == "string"){
3217             return el;
3218         }
3219         var id = el.id;
3220         if(!id && autogen !== false){
3221             id = "roodd-" + (++autoIdSeed);
3222             el.id = id;
3223         }
3224         return id;
3225     };
3226     
3227     return {
3228     /**
3229      * Register a drag drop element
3230      * @param {String|HTMLElement} element The id or DOM node to register
3231      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3232      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3233      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3234      * populated in the data object (if applicable):
3235      * <pre>
3236 Value      Description<br />
3237 ---------  ------------------------------------------<br />
3238 handles    Array of DOM nodes that trigger dragging<br />
3239            for the element being registered<br />
3240 isHandle   True if the element passed in triggers<br />
3241            dragging itself, else false
3242 </pre>
3243      */
3244         register : function(el, data){
3245             data = data || {};
3246             if(typeof el == "string"){
3247                 el = document.getElementById(el);
3248             }
3249             data.ddel = el;
3250             elements[getId(el)] = data;
3251             if(data.isHandle !== false){
3252                 handles[data.ddel.id] = data;
3253             }
3254             if(data.handles){
3255                 var hs = data.handles;
3256                 for(var i = 0, len = hs.length; i < len; i++){
3257                         handles[getId(hs[i])] = data;
3258                 }
3259             }
3260         },
3261
3262     /**
3263      * Unregister a drag drop element
3264      * @param {String|HTMLElement}  element The id or DOM node to unregister
3265      */
3266         unregister : function(el){
3267             var id = getId(el, false);
3268             var data = elements[id];
3269             if(data){
3270                 delete elements[id];
3271                 if(data.handles){
3272                     var hs = data.handles;
3273                     for(var i = 0, len = hs.length; i < len; i++){
3274                         delete handles[getId(hs[i], false)];
3275                     }
3276                 }
3277             }
3278         },
3279
3280     /**
3281      * Returns the handle registered for a DOM Node by id
3282      * @param {String|HTMLElement} id The DOM node or id to look up
3283      * @return {Object} handle The custom handle data
3284      */
3285         getHandle : function(id){
3286             if(typeof id != "string"){ // must be element?
3287                 id = id.id;
3288             }
3289             return handles[id];
3290         },
3291
3292     /**
3293      * Returns the handle that is registered for the DOM node that is the target of the event
3294      * @param {Event} e The event
3295      * @return {Object} handle The custom handle data
3296      */
3297         getHandleFromEvent : function(e){
3298             var t = Roo.lib.Event.getTarget(e);
3299             return t ? handles[t.id] : null;
3300         },
3301
3302     /**
3303      * Returns a custom data object that is registered for a DOM node by id
3304      * @param {String|HTMLElement} id The DOM node or id to look up
3305      * @return {Object} data The custom data
3306      */
3307         getTarget : function(id){
3308             if(typeof id != "string"){ // must be element?
3309                 id = id.id;
3310             }
3311             return elements[id];
3312         },
3313
3314     /**
3315      * Returns a custom data object that is registered for the DOM node that is the target of the event
3316      * @param {Event} e The event
3317      * @return {Object} data The custom data
3318      */
3319         getTargetFromEvent : function(e){
3320             var t = Roo.lib.Event.getTarget(e);
3321             return t ? elements[t.id] || handles[t.id] : null;
3322         }
3323     };
3324 }();/*
3325  * Based on:
3326  * Ext JS Library 1.1.1
3327  * Copyright(c) 2006-2007, Ext JS, LLC.
3328  *
3329  * Originally Released Under LGPL - original licence link has changed is not relivant.
3330  *
3331  * Fork - LGPL
3332  * <script type="text/javascript">
3333  */
3334  
3335
3336 /**
3337  * @class Roo.dd.StatusProxy
3338  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3339  * default drag proxy used by all Roo.dd components.
3340  * @constructor
3341  * @param {Object} config
3342  */
3343 Roo.dd.StatusProxy = function(config){
3344     Roo.apply(this, config);
3345     this.id = this.id || Roo.id();
3346     this.el = new Roo.Layer({
3347         dh: {
3348             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3349                 {tag: "div", cls: "x-dd-drop-icon"},
3350                 {tag: "div", cls: "x-dd-drag-ghost"}
3351             ]
3352         }, 
3353         shadow: !config || config.shadow !== false
3354     });
3355     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3356     this.dropStatus = this.dropNotAllowed;
3357 };
3358
3359 Roo.dd.StatusProxy.prototype = {
3360     /**
3361      * @cfg {String} dropAllowed
3362      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3363      */
3364     dropAllowed : "x-dd-drop-ok",
3365     /**
3366      * @cfg {String} dropNotAllowed
3367      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3368      */
3369     dropNotAllowed : "x-dd-drop-nodrop",
3370
3371     /**
3372      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3373      * over the current target element.
3374      * @param {String} cssClass The css class for the new drop status indicator image
3375      */
3376     setStatus : function(cssClass){
3377         cssClass = cssClass || this.dropNotAllowed;
3378         if(this.dropStatus != cssClass){
3379             this.el.replaceClass(this.dropStatus, cssClass);
3380             this.dropStatus = cssClass;
3381         }
3382     },
3383
3384     /**
3385      * Resets the status indicator to the default dropNotAllowed value
3386      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3387      */
3388     reset : function(clearGhost){
3389         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3390         this.dropStatus = this.dropNotAllowed;
3391         if(clearGhost){
3392             this.ghost.update("");
3393         }
3394     },
3395
3396     /**
3397      * Updates the contents of the ghost element
3398      * @param {String} html The html that will replace the current innerHTML of the ghost element
3399      */
3400     update : function(html){
3401         if(typeof html == "string"){
3402             this.ghost.update(html);
3403         }else{
3404             this.ghost.update("");
3405             html.style.margin = "0";
3406             this.ghost.dom.appendChild(html);
3407         }
3408         // ensure float = none set?? cant remember why though.
3409         var el = this.ghost.dom.firstChild;
3410                 if(el){
3411                         Roo.fly(el).setStyle('float', 'none');
3412                 }
3413     },
3414     
3415     /**
3416      * Returns the underlying proxy {@link Roo.Layer}
3417      * @return {Roo.Layer} el
3418     */
3419     getEl : function(){
3420         return this.el;
3421     },
3422
3423     /**
3424      * Returns the ghost element
3425      * @return {Roo.Element} el
3426      */
3427     getGhost : function(){
3428         return this.ghost;
3429     },
3430
3431     /**
3432      * Hides the proxy
3433      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3434      */
3435     hide : function(clear){
3436         this.el.hide();
3437         if(clear){
3438             this.reset(true);
3439         }
3440     },
3441
3442     /**
3443      * Stops the repair animation if it's currently running
3444      */
3445     stop : function(){
3446         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3447             this.anim.stop();
3448         }
3449     },
3450
3451     /**
3452      * Displays this proxy
3453      */
3454     show : function(){
3455         this.el.show();
3456     },
3457
3458     /**
3459      * Force the Layer to sync its shadow and shim positions to the element
3460      */
3461     sync : function(){
3462         this.el.sync();
3463     },
3464
3465     /**
3466      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3467      * invalid drop operation by the item being dragged.
3468      * @param {Array} xy The XY position of the element ([x, y])
3469      * @param {Function} callback The function to call after the repair is complete
3470      * @param {Object} scope The scope in which to execute the callback
3471      */
3472     repair : function(xy, callback, scope){
3473         this.callback = callback;
3474         this.scope = scope;
3475         if(xy && this.animRepair !== false){
3476             this.el.addClass("x-dd-drag-repair");
3477             this.el.hideUnders(true);
3478             this.anim = this.el.shift({
3479                 duration: this.repairDuration || .5,
3480                 easing: 'easeOut',
3481                 xy: xy,
3482                 stopFx: true,
3483                 callback: this.afterRepair,
3484                 scope: this
3485             });
3486         }else{
3487             this.afterRepair();
3488         }
3489     },
3490
3491     // private
3492     afterRepair : function(){
3493         this.hide(true);
3494         if(typeof this.callback == "function"){
3495             this.callback.call(this.scope || this);
3496         }
3497         this.callback = null;
3498         this.scope = null;
3499     }
3500 };/*
3501  * Based on:
3502  * Ext JS Library 1.1.1
3503  * Copyright(c) 2006-2007, Ext JS, LLC.
3504  *
3505  * Originally Released Under LGPL - original licence link has changed is not relivant.
3506  *
3507  * Fork - LGPL
3508  * <script type="text/javascript">
3509  */
3510
3511 /**
3512  * @class Roo.dd.DragSource
3513  * @extends Roo.dd.DDProxy
3514  * A simple class that provides the basic implementation needed to make any element draggable.
3515  * @constructor
3516  * @param {String/HTMLElement/Element} el The container element
3517  * @param {Object} config
3518  */
3519 Roo.dd.DragSource = function(el, config){
3520     this.el = Roo.get(el);
3521     this.dragData = {};
3522     
3523     Roo.apply(this, config);
3524     
3525     if(!this.proxy){
3526         this.proxy = new Roo.dd.StatusProxy();
3527     }
3528
3529     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3530           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3531     
3532     this.dragging = false;
3533 };
3534
3535 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3536     /**
3537      * @cfg {String} dropAllowed
3538      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3539      */
3540     dropAllowed : "x-dd-drop-ok",
3541     /**
3542      * @cfg {String} dropNotAllowed
3543      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3544      */
3545     dropNotAllowed : "x-dd-drop-nodrop",
3546
3547     /**
3548      * Returns the data object associated with this drag source
3549      * @return {Object} data An object containing arbitrary data
3550      */
3551     getDragData : function(e){
3552         return this.dragData;
3553     },
3554
3555     // private
3556     onDragEnter : function(e, id){
3557         var target = Roo.dd.DragDropMgr.getDDById(id);
3558         this.cachedTarget = target;
3559         if(this.beforeDragEnter(target, e, id) !== false){
3560             if(target.isNotifyTarget){
3561                 var status = target.notifyEnter(this, e, this.dragData);
3562                 this.proxy.setStatus(status);
3563             }else{
3564                 this.proxy.setStatus(this.dropAllowed);
3565             }
3566             
3567             if(this.afterDragEnter){
3568                 /**
3569                  * An empty function by default, but provided so that you can perform a custom action
3570                  * when the dragged item enters the drop target by providing an implementation.
3571                  * @param {Roo.dd.DragDrop} target The drop target
3572                  * @param {Event} e The event object
3573                  * @param {String} id The id of the dragged element
3574                  * @method afterDragEnter
3575                  */
3576                 this.afterDragEnter(target, e, id);
3577             }
3578         }
3579     },
3580
3581     /**
3582      * An empty function by default, but provided so that you can perform a custom action
3583      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3584      * @param {Roo.dd.DragDrop} target The drop target
3585      * @param {Event} e The event object
3586      * @param {String} id The id of the dragged element
3587      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3588      */
3589     beforeDragEnter : function(target, e, id){
3590         return true;
3591     },
3592
3593     // private
3594     alignElWithMouse: function() {
3595         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3596         this.proxy.sync();
3597     },
3598
3599     // private
3600     onDragOver : function(e, id){
3601         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3602         if(this.beforeDragOver(target, e, id) !== false){
3603             if(target.isNotifyTarget){
3604                 var status = target.notifyOver(this, e, this.dragData);
3605                 this.proxy.setStatus(status);
3606             }
3607
3608             if(this.afterDragOver){
3609                 /**
3610                  * An empty function by default, but provided so that you can perform a custom action
3611                  * while the dragged item is over the drop target by providing an implementation.
3612                  * @param {Roo.dd.DragDrop} target The drop target
3613                  * @param {Event} e The event object
3614                  * @param {String} id The id of the dragged element
3615                  * @method afterDragOver
3616                  */
3617                 this.afterDragOver(target, e, id);
3618             }
3619         }
3620     },
3621
3622     /**
3623      * An empty function by default, but provided so that you can perform a custom action
3624      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3625      * @param {Roo.dd.DragDrop} target The drop target
3626      * @param {Event} e The event object
3627      * @param {String} id The id of the dragged element
3628      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3629      */
3630     beforeDragOver : function(target, e, id){
3631         return true;
3632     },
3633
3634     // private
3635     onDragOut : function(e, id){
3636         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3637         if(this.beforeDragOut(target, e, id) !== false){
3638             if(target.isNotifyTarget){
3639                 target.notifyOut(this, e, this.dragData);
3640             }
3641             this.proxy.reset();
3642             if(this.afterDragOut){
3643                 /**
3644                  * An empty function by default, but provided so that you can perform a custom action
3645                  * after the dragged item is dragged out of the target without dropping.
3646                  * @param {Roo.dd.DragDrop} target The drop target
3647                  * @param {Event} e The event object
3648                  * @param {String} id The id of the dragged element
3649                  * @method afterDragOut
3650                  */
3651                 this.afterDragOut(target, e, id);
3652             }
3653         }
3654         this.cachedTarget = null;
3655     },
3656
3657     /**
3658      * An empty function by default, but provided so that you can perform a custom action before the dragged
3659      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3660      * @param {Roo.dd.DragDrop} target The drop target
3661      * @param {Event} e The event object
3662      * @param {String} id The id of the dragged element
3663      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3664      */
3665     beforeDragOut : function(target, e, id){
3666         return true;
3667     },
3668     
3669     // private
3670     onDragDrop : function(e, id){
3671         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3672         if(this.beforeDragDrop(target, e, id) !== false){
3673             if(target.isNotifyTarget){
3674                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3675                     this.onValidDrop(target, e, id);
3676                 }else{
3677                     this.onInvalidDrop(target, e, id);
3678                 }
3679             }else{
3680                 this.onValidDrop(target, e, id);
3681             }
3682             
3683             if(this.afterDragDrop){
3684                 /**
3685                  * An empty function by default, but provided so that you can perform a custom action
3686                  * after a valid drag drop has occurred by providing an implementation.
3687                  * @param {Roo.dd.DragDrop} target The drop target
3688                  * @param {Event} e The event object
3689                  * @param {String} id The id of the dropped element
3690                  * @method afterDragDrop
3691                  */
3692                 this.afterDragDrop(target, e, id);
3693             }
3694         }
3695         delete this.cachedTarget;
3696     },
3697
3698     /**
3699      * An empty function by default, but provided so that you can perform a custom action before the dragged
3700      * item is dropped onto the target and optionally cancel the onDragDrop.
3701      * @param {Roo.dd.DragDrop} target The drop target
3702      * @param {Event} e The event object
3703      * @param {String} id The id of the dragged element
3704      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3705      */
3706     beforeDragDrop : function(target, e, id){
3707         return true;
3708     },
3709
3710     // private
3711     onValidDrop : function(target, e, id){
3712         this.hideProxy();
3713         if(this.afterValidDrop){
3714             /**
3715              * An empty function by default, but provided so that you can perform a custom action
3716              * after a valid drop has occurred by providing an implementation.
3717              * @param {Object} target The target DD 
3718              * @param {Event} e The event object
3719              * @param {String} id The id of the dropped element
3720              * @method afterInvalidDrop
3721              */
3722             this.afterValidDrop(target, e, id);
3723         }
3724     },
3725
3726     // private
3727     getRepairXY : function(e, data){
3728         return this.el.getXY();  
3729     },
3730
3731     // private
3732     onInvalidDrop : function(target, e, id){
3733         this.beforeInvalidDrop(target, e, id);
3734         if(this.cachedTarget){
3735             if(this.cachedTarget.isNotifyTarget){
3736                 this.cachedTarget.notifyOut(this, e, this.dragData);
3737             }
3738             this.cacheTarget = null;
3739         }
3740         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3741
3742         if(this.afterInvalidDrop){
3743             /**
3744              * An empty function by default, but provided so that you can perform a custom action
3745              * after an invalid drop has occurred by providing an implementation.
3746              * @param {Event} e The event object
3747              * @param {String} id The id of the dropped element
3748              * @method afterInvalidDrop
3749              */
3750             this.afterInvalidDrop(e, id);
3751         }
3752     },
3753
3754     // private
3755     afterRepair : function(){
3756         if(Roo.enableFx){
3757             this.el.highlight(this.hlColor || "c3daf9");
3758         }
3759         this.dragging = false;
3760     },
3761
3762     /**
3763      * An empty function by default, but provided so that you can perform a custom action after an invalid
3764      * drop has occurred.
3765      * @param {Roo.dd.DragDrop} target The drop target
3766      * @param {Event} e The event object
3767      * @param {String} id The id of the dragged element
3768      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3769      */
3770     beforeInvalidDrop : function(target, e, id){
3771         return true;
3772     },
3773
3774     // private
3775     handleMouseDown : function(e){
3776         if(this.dragging) {
3777             return;
3778         }
3779         var data = this.getDragData(e);
3780         if(data && this.onBeforeDrag(data, e) !== false){
3781             this.dragData = data;
3782             this.proxy.stop();
3783             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3784         } 
3785     },
3786
3787     /**
3788      * An empty function by default, but provided so that you can perform a custom action before the initial
3789      * drag event begins and optionally cancel it.
3790      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3791      * @param {Event} e The event object
3792      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3793      */
3794     onBeforeDrag : function(data, e){
3795         return true;
3796     },
3797
3798     /**
3799      * An empty function by default, but provided so that you can perform a custom action once the initial
3800      * drag event has begun.  The drag cannot be canceled from this function.
3801      * @param {Number} x The x position of the click on the dragged object
3802      * @param {Number} y The y position of the click on the dragged object
3803      */
3804     onStartDrag : Roo.emptyFn,
3805
3806     // private - YUI override
3807     startDrag : function(x, y){
3808         this.proxy.reset();
3809         this.dragging = true;
3810         this.proxy.update("");
3811         this.onInitDrag(x, y);
3812         this.proxy.show();
3813     },
3814
3815     // private
3816     onInitDrag : function(x, y){
3817         var clone = this.el.dom.cloneNode(true);
3818         clone.id = Roo.id(); // prevent duplicate ids
3819         this.proxy.update(clone);
3820         this.onStartDrag(x, y);
3821         return true;
3822     },
3823
3824     /**
3825      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3826      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3827      */
3828     getProxy : function(){
3829         return this.proxy;  
3830     },
3831
3832     /**
3833      * Hides the drag source's {@link Roo.dd.StatusProxy}
3834      */
3835     hideProxy : function(){
3836         this.proxy.hide();  
3837         this.proxy.reset(true);
3838         this.dragging = false;
3839     },
3840
3841     // private
3842     triggerCacheRefresh : function(){
3843         Roo.dd.DDM.refreshCache(this.groups);
3844     },
3845
3846     // private - override to prevent hiding
3847     b4EndDrag: function(e) {
3848     },
3849
3850     // private - override to prevent moving
3851     endDrag : function(e){
3852         this.onEndDrag(this.dragData, e);
3853     },
3854
3855     // private
3856     onEndDrag : function(data, e){
3857     },
3858     
3859     // private - pin to cursor
3860     autoOffset : function(x, y) {
3861         this.setDelta(-12, -20);
3862     }    
3863 });/*
3864  * Based on:
3865  * Ext JS Library 1.1.1
3866  * Copyright(c) 2006-2007, Ext JS, LLC.
3867  *
3868  * Originally Released Under LGPL - original licence link has changed is not relivant.
3869  *
3870  * Fork - LGPL
3871  * <script type="text/javascript">
3872  */
3873
3874
3875 /**
3876  * @class Roo.dd.DropTarget
3877  * @extends Roo.dd.DDTarget
3878  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3879  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3880  * @constructor
3881  * @param {String/HTMLElement/Element} el The container element
3882  * @param {Object} config
3883  */
3884 Roo.dd.DropTarget = function(el, config){
3885     this.el = Roo.get(el);
3886     
3887     Roo.apply(this, config);
3888     
3889     if(this.containerScroll){
3890         Roo.dd.ScrollManager.register(this.el);
3891     }
3892     
3893     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
3894           {isTarget: true});
3895
3896 };
3897
3898 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3899     /**
3900      * @cfg {String} overClass
3901      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3902      */
3903     /**
3904      * @cfg {String} dropAllowed
3905      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3906      */
3907     dropAllowed : "x-dd-drop-ok",
3908     /**
3909      * @cfg {String} dropNotAllowed
3910      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3911      */
3912     dropNotAllowed : "x-dd-drop-nodrop",
3913
3914     // private
3915     isTarget : true,
3916
3917     // private
3918     isNotifyTarget : true,
3919
3920     /**
3921      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3922      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3923      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3924      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3925      * @param {Event} e The event
3926      * @param {Object} data An object containing arbitrary data supplied by the drag source
3927      * @return {String} status The CSS class that communicates the drop status back to the source so that the
3928      * underlying {@link Roo.dd.StatusProxy} can be updated
3929      */
3930     notifyEnter : function(dd, e, data){
3931         if(this.overClass){
3932             this.el.addClass(this.overClass);
3933         }
3934         return this.dropAllowed;
3935     },
3936
3937     /**
3938      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3939      * This method will be called on every mouse movement while the drag source is over the drop target.
3940      * This default implementation simply returns the dropAllowed config value.
3941      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3942      * @param {Event} e The event
3943      * @param {Object} data An object containing arbitrary data supplied by the drag source
3944      * @return {String} status The CSS class that communicates the drop status back to the source so that the
3945      * underlying {@link Roo.dd.StatusProxy} can be updated
3946      */
3947     notifyOver : function(dd, e, data){
3948         return this.dropAllowed;
3949     },
3950
3951     /**
3952      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3953      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3954      * overClass (if any) from the drop element.
3955      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3956      * @param {Event} e The event
3957      * @param {Object} data An object containing arbitrary data supplied by the drag source
3958      */
3959     notifyOut : function(dd, e, data){
3960         if(this.overClass){
3961             this.el.removeClass(this.overClass);
3962         }
3963     },
3964
3965     /**
3966      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3967      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3968      * implementation that does something to process the drop event and returns true so that the drag source's
3969      * repair action does not run.
3970      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3971      * @param {Event} e The event
3972      * @param {Object} data An object containing arbitrary data supplied by the drag source
3973      * @return {Boolean} True if the drop was valid, else false
3974      */
3975     notifyDrop : function(dd, e, data){
3976         return false;
3977     }
3978 });/*
3979  * Based on:
3980  * Ext JS Library 1.1.1
3981  * Copyright(c) 2006-2007, Ext JS, LLC.
3982  *
3983  * Originally Released Under LGPL - original licence link has changed is not relivant.
3984  *
3985  * Fork - LGPL
3986  * <script type="text/javascript">
3987  */
3988
3989
3990 /**
3991  * @class Roo.dd.DragZone
3992  * @extends Roo.dd.DragSource
3993  * This class provides a container DD instance that proxies for multiple child node sources.<br />
3994  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
3995  * @constructor
3996  * @param {String/HTMLElement/Element} el The container element
3997  * @param {Object} config
3998  */
3999 Roo.dd.DragZone = function(el, config){
4000     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4001     if(this.containerScroll){
4002         Roo.dd.ScrollManager.register(this.el);
4003     }
4004 };
4005
4006 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4007     /**
4008      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4009      * for auto scrolling during drag operations.
4010      */
4011     /**
4012      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4013      * method after a failed drop (defaults to "c3daf9" - light blue)
4014      */
4015
4016     /**
4017      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4018      * for a valid target to drag based on the mouse down. Override this method
4019      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4020      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4021      * @param {EventObject} e The mouse down event
4022      * @return {Object} The dragData
4023      */
4024     getDragData : function(e){
4025         return Roo.dd.Registry.getHandleFromEvent(e);
4026     },
4027     
4028     /**
4029      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4030      * this.dragData.ddel
4031      * @param {Number} x The x position of the click on the dragged object
4032      * @param {Number} y The y position of the click on the dragged object
4033      * @return {Boolean} true to continue the drag, false to cancel
4034      */
4035     onInitDrag : function(x, y){
4036         this.proxy.update(this.dragData.ddel.cloneNode(true));
4037         this.onStartDrag(x, y);
4038         return true;
4039     },
4040     
4041     /**
4042      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4043      */
4044     afterRepair : function(){
4045         if(Roo.enableFx){
4046             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4047         }
4048         this.dragging = false;
4049     },
4050
4051     /**
4052      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4053      * the XY of this.dragData.ddel
4054      * @param {EventObject} e The mouse up event
4055      * @return {Array} The xy location (e.g. [100, 200])
4056      */
4057     getRepairXY : function(e){
4058         return Roo.Element.fly(this.dragData.ddel).getXY();  
4059     }
4060 });/*
4061  * Based on:
4062  * Ext JS Library 1.1.1
4063  * Copyright(c) 2006-2007, Ext JS, LLC.
4064  *
4065  * Originally Released Under LGPL - original licence link has changed is not relivant.
4066  *
4067  * Fork - LGPL
4068  * <script type="text/javascript">
4069  */
4070 /**
4071  * @class Roo.dd.DropZone
4072  * @extends Roo.dd.DropTarget
4073  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4074  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4075  * @constructor
4076  * @param {String/HTMLElement/Element} el The container element
4077  * @param {Object} config
4078  */
4079 Roo.dd.DropZone = function(el, config){
4080     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4081 };
4082
4083 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4084     /**
4085      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4086      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4087      * provide your own custom lookup.
4088      * @param {Event} e The event
4089      * @return {Object} data The custom data
4090      */
4091     getTargetFromEvent : function(e){
4092         return Roo.dd.Registry.getTargetFromEvent(e);
4093     },
4094
4095     /**
4096      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4097      * that it has registered.  This method has no default implementation and should be overridden to provide
4098      * node-specific processing if necessary.
4099      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4100      * {@link #getTargetFromEvent} for this node)
4101      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4102      * @param {Event} e The event
4103      * @param {Object} data An object containing arbitrary data supplied by the drag source
4104      */
4105     onNodeEnter : function(n, dd, e, data){
4106         
4107     },
4108
4109     /**
4110      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4111      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4112      * overridden to provide the proper feedback.
4113      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4114      * {@link #getTargetFromEvent} for this node)
4115      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4116      * @param {Event} e The event
4117      * @param {Object} data An object containing arbitrary data supplied by the drag source
4118      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4119      * underlying {@link Roo.dd.StatusProxy} can be updated
4120      */
4121     onNodeOver : function(n, dd, e, data){
4122         return this.dropAllowed;
4123     },
4124
4125     /**
4126      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4127      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4128      * node-specific processing if necessary.
4129      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4130      * {@link #getTargetFromEvent} for this node)
4131      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4132      * @param {Event} e The event
4133      * @param {Object} data An object containing arbitrary data supplied by the drag source
4134      */
4135     onNodeOut : function(n, dd, e, data){
4136         
4137     },
4138
4139     /**
4140      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4141      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4142      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4143      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4144      * {@link #getTargetFromEvent} for this node)
4145      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4146      * @param {Event} e The event
4147      * @param {Object} data An object containing arbitrary data supplied by the drag source
4148      * @return {Boolean} True if the drop was valid, else false
4149      */
4150     onNodeDrop : function(n, dd, e, data){
4151         return false;
4152     },
4153
4154     /**
4155      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4156      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4157      * it should be overridden to provide the proper feedback if necessary.
4158      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4159      * @param {Event} e The event
4160      * @param {Object} data An object containing arbitrary data supplied by the drag source
4161      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4162      * underlying {@link Roo.dd.StatusProxy} can be updated
4163      */
4164     onContainerOver : function(dd, e, data){
4165         return this.dropNotAllowed;
4166     },
4167
4168     /**
4169      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4170      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4171      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4172      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4173      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4174      * @param {Event} e The event
4175      * @param {Object} data An object containing arbitrary data supplied by the drag source
4176      * @return {Boolean} True if the drop was valid, else false
4177      */
4178     onContainerDrop : function(dd, e, data){
4179         return false;
4180     },
4181
4182     /**
4183      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4184      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4185      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4186      * you should override this method and provide a custom implementation.
4187      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4188      * @param {Event} e The event
4189      * @param {Object} data An object containing arbitrary data supplied by the drag source
4190      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4191      * underlying {@link Roo.dd.StatusProxy} can be updated
4192      */
4193     notifyEnter : function(dd, e, data){
4194         return this.dropNotAllowed;
4195     },
4196
4197     /**
4198      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4199      * This method will be called on every mouse movement while the drag source is over the drop zone.
4200      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4201      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4202      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4203      * registered node, it will call {@link #onContainerOver}.
4204      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4205      * @param {Event} e The event
4206      * @param {Object} data An object containing arbitrary data supplied by the drag source
4207      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4208      * underlying {@link Roo.dd.StatusProxy} can be updated
4209      */
4210     notifyOver : function(dd, e, data){
4211         var n = this.getTargetFromEvent(e);
4212         if(!n){ // not over valid drop target
4213             if(this.lastOverNode){
4214                 this.onNodeOut(this.lastOverNode, dd, e, data);
4215                 this.lastOverNode = null;
4216             }
4217             return this.onContainerOver(dd, e, data);
4218         }
4219         if(this.lastOverNode != n){
4220             if(this.lastOverNode){
4221                 this.onNodeOut(this.lastOverNode, dd, e, data);
4222             }
4223             this.onNodeEnter(n, dd, e, data);
4224             this.lastOverNode = n;
4225         }
4226         return this.onNodeOver(n, dd, e, data);
4227     },
4228
4229     /**
4230      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4231      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4232      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4233      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4234      * @param {Event} e The event
4235      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4236      */
4237     notifyOut : function(dd, e, data){
4238         if(this.lastOverNode){
4239             this.onNodeOut(this.lastOverNode, dd, e, data);
4240             this.lastOverNode = null;
4241         }
4242     },
4243
4244     /**
4245      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4246      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4247      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4248      * otherwise it will call {@link #onContainerDrop}.
4249      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4250      * @param {Event} e The event
4251      * @param {Object} data An object containing arbitrary data supplied by the drag source
4252      * @return {Boolean} True if the drop was valid, else false
4253      */
4254     notifyDrop : function(dd, e, data){
4255         if(this.lastOverNode){
4256             this.onNodeOut(this.lastOverNode, dd, e, data);
4257             this.lastOverNode = null;
4258         }
4259         var n = this.getTargetFromEvent(e);
4260         return n ?
4261             this.onNodeDrop(n, dd, e, data) :
4262             this.onContainerDrop(dd, e, data);
4263     },
4264
4265     // private
4266     triggerCacheRefresh : function(){
4267         Roo.dd.DDM.refreshCache(this.groups);
4268     }  
4269 });/*
4270  * Based on:
4271  * Ext JS Library 1.1.1
4272  * Copyright(c) 2006-2007, Ext JS, LLC.
4273  *
4274  * Originally Released Under LGPL - original licence link has changed is not relivant.
4275  *
4276  * Fork - LGPL
4277  * <script type="text/javascript">
4278  */
4279
4280
4281 /**
4282  * @class Roo.data.SortTypes
4283  * @singleton
4284  * Defines the default sorting (casting?) comparison functions used when sorting data.
4285  */
4286 Roo.data.SortTypes = {
4287     /**
4288      * Default sort that does nothing
4289      * @param {Mixed} s The value being converted
4290      * @return {Mixed} The comparison value
4291      */
4292     none : function(s){
4293         return s;
4294     },
4295     
4296     /**
4297      * The regular expression used to strip tags
4298      * @type {RegExp}
4299      * @property
4300      */
4301     stripTagsRE : /<\/?[^>]+>/gi,
4302     
4303     /**
4304      * Strips all HTML tags to sort on text only
4305      * @param {Mixed} s The value being converted
4306      * @return {String} The comparison value
4307      */
4308     asText : function(s){
4309         return String(s).replace(this.stripTagsRE, "");
4310     },
4311     
4312     /**
4313      * Strips all HTML tags to sort on text only - Case insensitive
4314      * @param {Mixed} s The value being converted
4315      * @return {String} The comparison value
4316      */
4317     asUCText : function(s){
4318         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4319     },
4320     
4321     /**
4322      * Case insensitive string
4323      * @param {Mixed} s The value being converted
4324      * @return {String} The comparison value
4325      */
4326     asUCString : function(s) {
4327         return String(s).toUpperCase();
4328     },
4329     
4330     /**
4331      * Date sorting
4332      * @param {Mixed} s The value being converted
4333      * @return {Number} The comparison value
4334      */
4335     asDate : function(s) {
4336         if(!s){
4337             return 0;
4338         }
4339         if(s instanceof Date){
4340             return s.getTime();
4341         }
4342         return Date.parse(String(s));
4343     },
4344     
4345     /**
4346      * Float sorting
4347      * @param {Mixed} s The value being converted
4348      * @return {Float} The comparison value
4349      */
4350     asFloat : function(s) {
4351         var val = parseFloat(String(s).replace(/,/g, ""));
4352         if(isNaN(val)) val = 0;
4353         return val;
4354     },
4355     
4356     /**
4357      * Integer sorting
4358      * @param {Mixed} s The value being converted
4359      * @return {Number} The comparison value
4360      */
4361     asInt : function(s) {
4362         var val = parseInt(String(s).replace(/,/g, ""));
4363         if(isNaN(val)) val = 0;
4364         return val;
4365     }
4366 };/*
4367  * Based on:
4368  * Ext JS Library 1.1.1
4369  * Copyright(c) 2006-2007, Ext JS, LLC.
4370  *
4371  * Originally Released Under LGPL - original licence link has changed is not relivant.
4372  *
4373  * Fork - LGPL
4374  * <script type="text/javascript">
4375  */
4376
4377 /**
4378 * @class Roo.data.Record
4379  * Instances of this class encapsulate both record <em>definition</em> information, and record
4380  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4381  * to access Records cached in an {@link Roo.data.Store} object.<br>
4382  * <p>
4383  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4384  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4385  * objects.<br>
4386  * <p>
4387  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4388  * @constructor
4389  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4390  * {@link #create}. The parameters are the same.
4391  * @param {Array} data An associative Array of data values keyed by the field name.
4392  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4393  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4394  * not specified an integer id is generated.
4395  */
4396 Roo.data.Record = function(data, id){
4397     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4398     this.data = data;
4399 };
4400
4401 /**
4402  * Generate a constructor for a specific record layout.
4403  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4404  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4405  * Each field definition object may contain the following properties: <ul>
4406  * <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,
4407  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4408  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4409  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4410  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4411  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4412  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4413  * this may be omitted.</p></li>
4414  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4415  * <ul><li>auto (Default, implies no conversion)</li>
4416  * <li>string</li>
4417  * <li>int</li>
4418  * <li>float</li>
4419  * <li>boolean</li>
4420  * <li>date</li></ul></p></li>
4421  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4422  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4423  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4424  * by the Reader into an object that will be stored in the Record. It is passed the
4425  * following parameters:<ul>
4426  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4427  * </ul></p></li>
4428  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4429  * </ul>
4430  * <br>usage:<br><pre><code>
4431 var TopicRecord = Roo.data.Record.create(
4432     {name: 'title', mapping: 'topic_title'},
4433     {name: 'author', mapping: 'username'},
4434     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4435     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4436     {name: 'lastPoster', mapping: 'user2'},
4437     {name: 'excerpt', mapping: 'post_text'}
4438 );
4439
4440 var myNewRecord = new TopicRecord({
4441     title: 'Do my job please',
4442     author: 'noobie',
4443     totalPosts: 1,
4444     lastPost: new Date(),
4445     lastPoster: 'Animal',
4446     excerpt: 'No way dude!'
4447 });
4448 myStore.add(myNewRecord);
4449 </code></pre>
4450  * @method create
4451  * @static
4452  */
4453 Roo.data.Record.create = function(o){
4454     var f = function(){
4455         f.superclass.constructor.apply(this, arguments);
4456     };
4457     Roo.extend(f, Roo.data.Record);
4458     var p = f.prototype;
4459     p.fields = new Roo.util.MixedCollection(false, function(field){
4460         return field.name;
4461     });
4462     for(var i = 0, len = o.length; i < len; i++){
4463         p.fields.add(new Roo.data.Field(o[i]));
4464     }
4465     f.getField = function(name){
4466         return p.fields.get(name);  
4467     };
4468     return f;
4469 };
4470
4471 Roo.data.Record.AUTO_ID = 1000;
4472 Roo.data.Record.EDIT = 'edit';
4473 Roo.data.Record.REJECT = 'reject';
4474 Roo.data.Record.COMMIT = 'commit';
4475
4476 Roo.data.Record.prototype = {
4477     /**
4478      * Readonly flag - true if this record has been modified.
4479      * @type Boolean
4480      */
4481     dirty : false,
4482     editing : false,
4483     error: null,
4484     modified: null,
4485
4486     // private
4487     join : function(store){
4488         this.store = store;
4489     },
4490
4491     /**
4492      * Set the named field to the specified value.
4493      * @param {String} name The name of the field to set.
4494      * @param {Object} value The value to set the field to.
4495      */
4496     set : function(name, value){
4497         if(this.data[name] == value){
4498             return;
4499         }
4500         this.dirty = true;
4501         if(!this.modified){
4502             this.modified = {};
4503         }
4504         if(typeof this.modified[name] == 'undefined'){
4505             this.modified[name] = this.data[name];
4506         }
4507         this.data[name] = value;
4508         if(!this.editing){
4509             this.store.afterEdit(this);
4510         }       
4511     },
4512
4513     /**
4514      * Get the value of the named field.
4515      * @param {String} name The name of the field to get the value of.
4516      * @return {Object} The value of the field.
4517      */
4518     get : function(name){
4519         return this.data[name]; 
4520     },
4521
4522     // private
4523     beginEdit : function(){
4524         this.editing = true;
4525         this.modified = {}; 
4526     },
4527
4528     // private
4529     cancelEdit : function(){
4530         this.editing = false;
4531         delete this.modified;
4532     },
4533
4534     // private
4535     endEdit : function(){
4536         this.editing = false;
4537         if(this.dirty && this.store){
4538             this.store.afterEdit(this);
4539         }
4540     },
4541
4542     /**
4543      * Usually called by the {@link Roo.data.Store} which owns the Record.
4544      * Rejects all changes made to the Record since either creation, or the last commit operation.
4545      * Modified fields are reverted to their original values.
4546      * <p>
4547      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4548      * of reject operations.
4549      */
4550     reject : function(){
4551         var m = this.modified;
4552         for(var n in m){
4553             if(typeof m[n] != "function"){
4554                 this.data[n] = m[n];
4555             }
4556         }
4557         this.dirty = false;
4558         delete this.modified;
4559         this.editing = false;
4560         if(this.store){
4561             this.store.afterReject(this);
4562         }
4563     },
4564
4565     /**
4566      * Usually called by the {@link Roo.data.Store} which owns the Record.
4567      * Commits all changes made to the Record since either creation, or the last commit operation.
4568      * <p>
4569      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4570      * of commit operations.
4571      */
4572     commit : function(){
4573         this.dirty = false;
4574         delete this.modified;
4575         this.editing = false;
4576         if(this.store){
4577             this.store.afterCommit(this);
4578         }
4579     },
4580
4581     // private
4582     hasError : function(){
4583         return this.error != null;
4584     },
4585
4586     // private
4587     clearError : function(){
4588         this.error = null;
4589     },
4590
4591     /**
4592      * Creates a copy of this record.
4593      * @param {String} id (optional) A new record id if you don't want to use this record's id
4594      * @return {Record}
4595      */
4596     copy : function(newId) {
4597         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4598     }
4599 };/*
4600  * Based on:
4601  * Ext JS Library 1.1.1
4602  * Copyright(c) 2006-2007, Ext JS, LLC.
4603  *
4604  * Originally Released Under LGPL - original licence link has changed is not relivant.
4605  *
4606  * Fork - LGPL
4607  * <script type="text/javascript">
4608  */
4609
4610
4611
4612 /**
4613  * @class Roo.data.Store
4614  * @extends Roo.util.Observable
4615  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4616  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4617  * <p>
4618  * 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
4619  * has no knowledge of the format of the data returned by the Proxy.<br>
4620  * <p>
4621  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4622  * instances from the data object. These records are cached and made available through accessor functions.
4623  * @constructor
4624  * Creates a new Store.
4625  * @param {Object} config A config object containing the objects needed for the Store to access data,
4626  * and read the data into Records.
4627  */
4628 Roo.data.Store = function(config){
4629     this.data = new Roo.util.MixedCollection(false);
4630     this.data.getKey = function(o){
4631         return o.id;
4632     };
4633     this.baseParams = {};
4634     // private
4635     this.paramNames = {
4636         "start" : "start",
4637         "limit" : "limit",
4638         "sort" : "sort",
4639         "dir" : "dir"
4640     };
4641
4642     if(config && config.data){
4643         this.inlineData = config.data;
4644         delete config.data;
4645     }
4646
4647     Roo.apply(this, config);
4648     
4649     if(this.reader){ // reader passed
4650         this.reader = Roo.factory(this.reader, Roo.data);
4651         this.reader.xmodule = this.xmodule || false;
4652         if(!this.recordType){
4653             this.recordType = this.reader.recordType;
4654         }
4655         if(this.reader.onMetaChange){
4656             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4657         }
4658     }
4659
4660     if(this.recordType){
4661         this.fields = this.recordType.prototype.fields;
4662     }
4663     this.modified = [];
4664
4665     this.addEvents({
4666         /**
4667          * @event datachanged
4668          * Fires when the data cache has changed, and a widget which is using this Store
4669          * as a Record cache should refresh its view.
4670          * @param {Store} this
4671          */
4672         datachanged : true,
4673         /**
4674          * @event metachange
4675          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4676          * @param {Store} this
4677          * @param {Object} meta The JSON metadata
4678          */
4679         metachange : true,
4680         /**
4681          * @event add
4682          * Fires when Records have been added to the Store
4683          * @param {Store} this
4684          * @param {Roo.data.Record[]} records The array of Records added
4685          * @param {Number} index The index at which the record(s) were added
4686          */
4687         add : true,
4688         /**
4689          * @event remove
4690          * Fires when a Record has been removed from the Store
4691          * @param {Store} this
4692          * @param {Roo.data.Record} record The Record that was removed
4693          * @param {Number} index The index at which the record was removed
4694          */
4695         remove : true,
4696         /**
4697          * @event update
4698          * Fires when a Record has been updated
4699          * @param {Store} this
4700          * @param {Roo.data.Record} record The Record that was updated
4701          * @param {String} operation The update operation being performed.  Value may be one of:
4702          * <pre><code>
4703  Roo.data.Record.EDIT
4704  Roo.data.Record.REJECT
4705  Roo.data.Record.COMMIT
4706          * </code></pre>
4707          */
4708         update : true,
4709         /**
4710          * @event clear
4711          * Fires when the data cache has been cleared.
4712          * @param {Store} this
4713          */
4714         clear : true,
4715         /**
4716          * @event beforeload
4717          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4718          * the load action will be canceled.
4719          * @param {Store} this
4720          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4721          */
4722         beforeload : true,
4723         /**
4724          * @event load
4725          * Fires after a new set of Records has been loaded.
4726          * @param {Store} this
4727          * @param {Roo.data.Record[]} records The Records that were loaded
4728          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4729          */
4730         load : true,
4731         /**
4732          * @event loadexception
4733          * Fires if an exception occurs in the Proxy during loading.
4734          * Called with the signature of the Proxy's "loadexception" event.
4735          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4736          * 
4737          * @param {Proxy} 
4738          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4739          * @param {Object} load options 
4740          * @param {Object} jsonData from your request (normally this contains the Exception)
4741          */
4742         loadexception : true
4743     });
4744     
4745     if(this.proxy){
4746         this.proxy = Roo.factory(this.proxy, Roo.data);
4747         this.proxy.xmodule = this.xmodule || false;
4748         this.relayEvents(this.proxy,  ["loadexception"]);
4749     }
4750     this.sortToggle = {};
4751
4752     Roo.data.Store.superclass.constructor.call(this);
4753
4754     if(this.inlineData){
4755         this.loadData(this.inlineData);
4756         delete this.inlineData;
4757     }
4758 };
4759 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4760      /**
4761     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4762     * without a remote query - used by combo/forms at present.
4763     */
4764     
4765     /**
4766     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4767     */
4768     /**
4769     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4770     */
4771     /**
4772     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4773     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4774     */
4775     /**
4776     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4777     * on any HTTP request
4778     */
4779     /**
4780     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4781     */
4782     /**
4783     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4784     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4785     */
4786     remoteSort : false,
4787
4788     /**
4789     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4790      * loaded or when a record is removed. (defaults to false).
4791     */
4792     pruneModifiedRecords : false,
4793
4794     // private
4795     lastOptions : null,
4796
4797     /**
4798      * Add Records to the Store and fires the add event.
4799      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4800      */
4801     add : function(records){
4802         records = [].concat(records);
4803         for(var i = 0, len = records.length; i < len; i++){
4804             records[i].join(this);
4805         }
4806         var index = this.data.length;
4807         this.data.addAll(records);
4808         this.fireEvent("add", this, records, index);
4809     },
4810
4811     /**
4812      * Remove a Record from the Store and fires the remove event.
4813      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4814      */
4815     remove : function(record){
4816         var index = this.data.indexOf(record);
4817         this.data.removeAt(index);
4818         if(this.pruneModifiedRecords){
4819             this.modified.remove(record);
4820         }
4821         this.fireEvent("remove", this, record, index);
4822     },
4823
4824     /**
4825      * Remove all Records from the Store and fires the clear event.
4826      */
4827     removeAll : function(){
4828         this.data.clear();
4829         if(this.pruneModifiedRecords){
4830             this.modified = [];
4831         }
4832         this.fireEvent("clear", this);
4833     },
4834
4835     /**
4836      * Inserts Records to the Store at the given index and fires the add event.
4837      * @param {Number} index The start index at which to insert the passed Records.
4838      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4839      */
4840     insert : function(index, records){
4841         records = [].concat(records);
4842         for(var i = 0, len = records.length; i < len; i++){
4843             this.data.insert(index, records[i]);
4844             records[i].join(this);
4845         }
4846         this.fireEvent("add", this, records, index);
4847     },
4848
4849     /**
4850      * Get the index within the cache of the passed Record.
4851      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4852      * @return {Number} The index of the passed Record. Returns -1 if not found.
4853      */
4854     indexOf : function(record){
4855         return this.data.indexOf(record);
4856     },
4857
4858     /**
4859      * Get the index within the cache of the Record with the passed id.
4860      * @param {String} id The id of the Record to find.
4861      * @return {Number} The index of the Record. Returns -1 if not found.
4862      */
4863     indexOfId : function(id){
4864         return this.data.indexOfKey(id);
4865     },
4866
4867     /**
4868      * Get the Record with the specified id.
4869      * @param {String} id The id of the Record to find.
4870      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4871      */
4872     getById : function(id){
4873         return this.data.key(id);
4874     },
4875
4876     /**
4877      * Get the Record at the specified index.
4878      * @param {Number} index The index of the Record to find.
4879      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4880      */
4881     getAt : function(index){
4882         return this.data.itemAt(index);
4883     },
4884
4885     /**
4886      * Returns a range of Records between specified indices.
4887      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4888      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4889      * @return {Roo.data.Record[]} An array of Records
4890      */
4891     getRange : function(start, end){
4892         return this.data.getRange(start, end);
4893     },
4894
4895     // private
4896     storeOptions : function(o){
4897         o = Roo.apply({}, o);
4898         delete o.callback;
4899         delete o.scope;
4900         this.lastOptions = o;
4901     },
4902
4903     /**
4904      * Loads the Record cache from the configured Proxy using the configured Reader.
4905      * <p>
4906      * If using remote paging, then the first load call must specify the <em>start</em>
4907      * and <em>limit</em> properties in the options.params property to establish the initial
4908      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4909      * <p>
4910      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4911      * and this call will return before the new data has been loaded. Perform any post-processing
4912      * in a callback function, or in a "load" event handler.</strong>
4913      * <p>
4914      * @param {Object} options An object containing properties which control loading options:<ul>
4915      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4916      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4917      * passed the following arguments:<ul>
4918      * <li>r : Roo.data.Record[]</li>
4919      * <li>options: Options object from the load call</li>
4920      * <li>success: Boolean success indicator</li></ul></li>
4921      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4922      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4923      * </ul>
4924      */
4925     load : function(options){
4926         options = options || {};
4927         if(this.fireEvent("beforeload", this, options) !== false){
4928             this.storeOptions(options);
4929             var p = Roo.apply(options.params || {}, this.baseParams);
4930             // if meta was not loaded from remote source.. try requesting it.
4931             if (!this.reader.metaFromRemote) {
4932                 p._requestMeta = 1;
4933             }
4934             if(this.sortInfo && this.remoteSort){
4935                 var pn = this.paramNames;
4936                 p[pn["sort"]] = this.sortInfo.field;
4937                 p[pn["dir"]] = this.sortInfo.direction;
4938             }
4939             this.proxy.load(p, this.reader, this.loadRecords, this, options);
4940         }
4941     },
4942
4943     /**
4944      * Reloads the Record cache from the configured Proxy using the configured Reader and
4945      * the options from the last load operation performed.
4946      * @param {Object} options (optional) An object containing properties which may override the options
4947      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
4948      * the most recently used options are reused).
4949      */
4950     reload : function(options){
4951         this.load(Roo.applyIf(options||{}, this.lastOptions));
4952     },
4953
4954     // private
4955     // Called as a callback by the Reader during a load operation.
4956     loadRecords : function(o, options, success){
4957         if(!o || success === false){
4958             if(success !== false){
4959                 this.fireEvent("load", this, [], options);
4960             }
4961             if(options.callback){
4962                 options.callback.call(options.scope || this, [], options, false);
4963             }
4964             return;
4965         }
4966         // if data returned failure - throw an exception.
4967         if (o.success === false) {
4968             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
4969             return;
4970         }
4971         var r = o.records, t = o.totalRecords || r.length;
4972         if(!options || options.add !== true){
4973             if(this.pruneModifiedRecords){
4974                 this.modified = [];
4975             }
4976             for(var i = 0, len = r.length; i < len; i++){
4977                 r[i].join(this);
4978             }
4979             if(this.snapshot){
4980                 this.data = this.snapshot;
4981                 delete this.snapshot;
4982             }
4983             this.data.clear();
4984             this.data.addAll(r);
4985             this.totalLength = t;
4986             this.applySort();
4987             this.fireEvent("datachanged", this);
4988         }else{
4989             this.totalLength = Math.max(t, this.data.length+r.length);
4990             this.add(r);
4991         }
4992         this.fireEvent("load", this, r, options);
4993         if(options.callback){
4994             options.callback.call(options.scope || this, r, options, true);
4995         }
4996     },
4997
4998     /**
4999      * Loads data from a passed data block. A Reader which understands the format of the data
5000      * must have been configured in the constructor.
5001      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5002      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5003      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5004      */
5005     loadData : function(o, append){
5006         var r = this.reader.readRecords(o);
5007         this.loadRecords(r, {add: append}, true);
5008     },
5009
5010     /**
5011      * Gets the number of cached records.
5012      * <p>
5013      * <em>If using paging, this may not be the total size of the dataset. If the data object
5014      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5015      * the data set size</em>
5016      */
5017     getCount : function(){
5018         return this.data.length || 0;
5019     },
5020
5021     /**
5022      * Gets the total number of records in the dataset as returned by the server.
5023      * <p>
5024      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5025      * the dataset size</em>
5026      */
5027     getTotalCount : function(){
5028         return this.totalLength || 0;
5029     },
5030
5031     /**
5032      * Returns the sort state of the Store as an object with two properties:
5033      * <pre><code>
5034  field {String} The name of the field by which the Records are sorted
5035  direction {String} The sort order, "ASC" or "DESC"
5036      * </code></pre>
5037      */
5038     getSortState : function(){
5039         return this.sortInfo;
5040     },
5041
5042     // private
5043     applySort : function(){
5044         if(this.sortInfo && !this.remoteSort){
5045             var s = this.sortInfo, f = s.field;
5046             var st = this.fields.get(f).sortType;
5047             var fn = function(r1, r2){
5048                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5049                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5050             };
5051             this.data.sort(s.direction, fn);
5052             if(this.snapshot && this.snapshot != this.data){
5053                 this.snapshot.sort(s.direction, fn);
5054             }
5055         }
5056     },
5057
5058     /**
5059      * Sets the default sort column and order to be used by the next load operation.
5060      * @param {String} fieldName The name of the field to sort by.
5061      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5062      */
5063     setDefaultSort : function(field, dir){
5064         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5065     },
5066
5067     /**
5068      * Sort the Records.
5069      * If remote sorting is used, the sort is performed on the server, and the cache is
5070      * reloaded. If local sorting is used, the cache is sorted internally.
5071      * @param {String} fieldName The name of the field to sort by.
5072      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5073      */
5074     sort : function(fieldName, dir){
5075         var f = this.fields.get(fieldName);
5076         if(!dir){
5077             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5078                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5079             }else{
5080                 dir = f.sortDir;
5081             }
5082         }
5083         this.sortToggle[f.name] = dir;
5084         this.sortInfo = {field: f.name, direction: dir};
5085         if(!this.remoteSort){
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.load(this.lastOptions);
5090         }
5091     },
5092
5093     /**
5094      * Calls the specified function for each of the Records in the cache.
5095      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5096      * Returning <em>false</em> aborts and exits the iteration.
5097      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5098      */
5099     each : function(fn, scope){
5100         this.data.each(fn, scope);
5101     },
5102
5103     /**
5104      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5105      * (e.g., during paging).
5106      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5107      */
5108     getModifiedRecords : function(){
5109         return this.modified;
5110     },
5111
5112     // private
5113     createFilterFn : function(property, value, anyMatch){
5114         if(!value.exec){ // not a regex
5115             value = String(value);
5116             if(value.length == 0){
5117                 return false;
5118             }
5119             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5120         }
5121         return function(r){
5122             return value.test(r.data[property]);
5123         };
5124     },
5125
5126     /**
5127      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5128      * @param {String} property A field on your records
5129      * @param {Number} start The record index to start at (defaults to 0)
5130      * @param {Number} end The last record index to include (defaults to length - 1)
5131      * @return {Number} The sum
5132      */
5133     sum : function(property, start, end){
5134         var rs = this.data.items, v = 0;
5135         start = start || 0;
5136         end = (end || end === 0) ? end : rs.length-1;
5137
5138         for(var i = start; i <= end; i++){
5139             v += (rs[i].data[property] || 0);
5140         }
5141         return v;
5142     },
5143
5144     /**
5145      * Filter the records by a specified property.
5146      * @param {String} field A field on your records
5147      * @param {String/RegExp} value Either a string that the field
5148      * should start with or a RegExp to test against the field
5149      * @param {Boolean} anyMatch True to match any part not just the beginning
5150      */
5151     filter : function(property, value, anyMatch){
5152         var fn = this.createFilterFn(property, value, anyMatch);
5153         return fn ? this.filterBy(fn) : this.clearFilter();
5154     },
5155
5156     /**
5157      * Filter by a function. The specified function will be called with each
5158      * record in this data source. If the function returns true the record is included,
5159      * otherwise it is filtered.
5160      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5161      * @param {Object} scope (optional) The scope of the function (defaults to this)
5162      */
5163     filterBy : function(fn, scope){
5164         this.snapshot = this.snapshot || this.data;
5165         this.data = this.queryBy(fn, scope||this);
5166         this.fireEvent("datachanged", this);
5167     },
5168
5169     /**
5170      * Query the records by a specified property.
5171      * @param {String} field A field on your records
5172      * @param {String/RegExp} value Either a string that the field
5173      * should start with or a RegExp to test against the field
5174      * @param {Boolean} anyMatch True to match any part not just the beginning
5175      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5176      */
5177     query : function(property, value, anyMatch){
5178         var fn = this.createFilterFn(property, value, anyMatch);
5179         return fn ? this.queryBy(fn) : this.data.clone();
5180     },
5181
5182     /**
5183      * Query by a function. The specified function will be called with each
5184      * record in this data source. If the function returns true the record is included
5185      * in the results.
5186      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5187      * @param {Object} scope (optional) The scope of the function (defaults to this)
5188       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5189      **/
5190     queryBy : function(fn, scope){
5191         var data = this.snapshot || this.data;
5192         return data.filterBy(fn, scope||this);
5193     },
5194
5195     /**
5196      * Collects unique values for a particular dataIndex from this store.
5197      * @param {String} dataIndex The property to collect
5198      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5199      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5200      * @return {Array} An array of the unique values
5201      **/
5202     collect : function(dataIndex, allowNull, bypassFilter){
5203         var d = (bypassFilter === true && this.snapshot) ?
5204                 this.snapshot.items : this.data.items;
5205         var v, sv, r = [], l = {};
5206         for(var i = 0, len = d.length; i < len; i++){
5207             v = d[i].data[dataIndex];
5208             sv = String(v);
5209             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5210                 l[sv] = true;
5211                 r[r.length] = v;
5212             }
5213         }
5214         return r;
5215     },
5216
5217     /**
5218      * Revert to a view of the Record cache with no filtering applied.
5219      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5220      */
5221     clearFilter : function(suppressEvent){
5222         if(this.snapshot && this.snapshot != this.data){
5223             this.data = this.snapshot;
5224             delete this.snapshot;
5225             if(suppressEvent !== true){
5226                 this.fireEvent("datachanged", this);
5227             }
5228         }
5229     },
5230
5231     // private
5232     afterEdit : function(record){
5233         if(this.modified.indexOf(record) == -1){
5234             this.modified.push(record);
5235         }
5236         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5237     },
5238
5239     // private
5240     afterReject : function(record){
5241         this.modified.remove(record);
5242         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5243     },
5244
5245     // private
5246     afterCommit : function(record){
5247         this.modified.remove(record);
5248         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5249     },
5250
5251     /**
5252      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5253      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5254      */
5255     commitChanges : function(){
5256         var m = this.modified.slice(0);
5257         this.modified = [];
5258         for(var i = 0, len = m.length; i < len; i++){
5259             m[i].commit();
5260         }
5261     },
5262
5263     /**
5264      * Cancel outstanding changes on all changed records.
5265      */
5266     rejectChanges : function(){
5267         var m = this.modified.slice(0);
5268         this.modified = [];
5269         for(var i = 0, len = m.length; i < len; i++){
5270             m[i].reject();
5271         }
5272     },
5273
5274     onMetaChange : function(meta, rtype, o){
5275         this.recordType = rtype;
5276         this.fields = rtype.prototype.fields;
5277         delete this.snapshot;
5278         this.sortInfo = meta.sortInfo || this.sortInfo;
5279         this.modified = [];
5280         this.fireEvent('metachange', this, this.reader.meta);
5281     }
5282 });/*
5283  * Based on:
5284  * Ext JS Library 1.1.1
5285  * Copyright(c) 2006-2007, Ext JS, LLC.
5286  *
5287  * Originally Released Under LGPL - original licence link has changed is not relivant.
5288  *
5289  * Fork - LGPL
5290  * <script type="text/javascript">
5291  */
5292
5293 /**
5294  * @class Roo.data.SimpleStore
5295  * @extends Roo.data.Store
5296  * Small helper class to make creating Stores from Array data easier.
5297  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5298  * @cfg {Array} fields An array of field definition objects, or field name strings.
5299  * @cfg {Array} data The multi-dimensional array of data
5300  * @constructor
5301  * @param {Object} config
5302  */
5303 Roo.data.SimpleStore = function(config){
5304     Roo.data.SimpleStore.superclass.constructor.call(this, {
5305         isLocal : true,
5306         reader: new Roo.data.ArrayReader({
5307                 id: config.id
5308             },
5309             Roo.data.Record.create(config.fields)
5310         ),
5311         proxy : new Roo.data.MemoryProxy(config.data)
5312     });
5313     this.load();
5314 };
5315 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5316  * Based on:
5317  * Ext JS Library 1.1.1
5318  * Copyright(c) 2006-2007, Ext JS, LLC.
5319  *
5320  * Originally Released Under LGPL - original licence link has changed is not relivant.
5321  *
5322  * Fork - LGPL
5323  * <script type="text/javascript">
5324  */
5325
5326 /**
5327 /**
5328  * @extends Roo.data.Store
5329  * @class Roo.data.JsonStore
5330  * Small helper class to make creating Stores for JSON data easier. <br/>
5331 <pre><code>
5332 var store = new Roo.data.JsonStore({
5333     url: 'get-images.php',
5334     root: 'images',
5335     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5336 });
5337 </code></pre>
5338  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5339  * JsonReader and HttpProxy (unless inline data is provided).</b>
5340  * @cfg {Array} fields An array of field definition objects, or field name strings.
5341  * @constructor
5342  * @param {Object} config
5343  */
5344 Roo.data.JsonStore = function(c){
5345     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5346         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5347         reader: new Roo.data.JsonReader(c, c.fields)
5348     }));
5349 };
5350 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5351  * Based on:
5352  * Ext JS Library 1.1.1
5353  * Copyright(c) 2006-2007, Ext JS, LLC.
5354  *
5355  * Originally Released Under LGPL - original licence link has changed is not relivant.
5356  *
5357  * Fork - LGPL
5358  * <script type="text/javascript">
5359  */
5360
5361  
5362 Roo.data.Field = function(config){
5363     if(typeof config == "string"){
5364         config = {name: config};
5365     }
5366     Roo.apply(this, config);
5367     
5368     if(!this.type){
5369         this.type = "auto";
5370     }
5371     
5372     var st = Roo.data.SortTypes;
5373     // named sortTypes are supported, here we look them up
5374     if(typeof this.sortType == "string"){
5375         this.sortType = st[this.sortType];
5376     }
5377     
5378     // set default sortType for strings and dates
5379     if(!this.sortType){
5380         switch(this.type){
5381             case "string":
5382                 this.sortType = st.asUCString;
5383                 break;
5384             case "date":
5385                 this.sortType = st.asDate;
5386                 break;
5387             default:
5388                 this.sortType = st.none;
5389         }
5390     }
5391
5392     // define once
5393     var stripRe = /[\$,%]/g;
5394
5395     // prebuilt conversion function for this field, instead of
5396     // switching every time we're reading a value
5397     if(!this.convert){
5398         var cv, dateFormat = this.dateFormat;
5399         switch(this.type){
5400             case "":
5401             case "auto":
5402             case undefined:
5403                 cv = function(v){ return v; };
5404                 break;
5405             case "string":
5406                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5407                 break;
5408             case "int":
5409                 cv = function(v){
5410                     return v !== undefined && v !== null && v !== '' ?
5411                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5412                     };
5413                 break;
5414             case "float":
5415                 cv = function(v){
5416                     return v !== undefined && v !== null && v !== '' ?
5417                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5418                     };
5419                 break;
5420             case "bool":
5421             case "boolean":
5422                 cv = function(v){ return v === true || v === "true" || v == 1; };
5423                 break;
5424             case "date":
5425                 cv = function(v){
5426                     if(!v){
5427                         return '';
5428                     }
5429                     if(v instanceof Date){
5430                         return v;
5431                     }
5432                     if(dateFormat){
5433                         if(dateFormat == "timestamp"){
5434                             return new Date(v*1000);
5435                         }
5436                         return Date.parseDate(v, dateFormat);
5437                     }
5438                     var parsed = Date.parse(v);
5439                     return parsed ? new Date(parsed) : null;
5440                 };
5441              break;
5442             
5443         }
5444         this.convert = cv;
5445     }
5446 };
5447
5448 Roo.data.Field.prototype = {
5449     dateFormat: null,
5450     defaultValue: "",
5451     mapping: null,
5452     sortType : null,
5453     sortDir : "ASC"
5454 };/*
5455  * Based on:
5456  * Ext JS Library 1.1.1
5457  * Copyright(c) 2006-2007, Ext JS, LLC.
5458  *
5459  * Originally Released Under LGPL - original licence link has changed is not relivant.
5460  *
5461  * Fork - LGPL
5462  * <script type="text/javascript">
5463  */
5464  
5465 // Base class for reading structured data from a data source.  This class is intended to be
5466 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5467
5468 /**
5469  * @class Roo.data.DataReader
5470  * Base class for reading structured data from a data source.  This class is intended to be
5471  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5472  */
5473
5474 Roo.data.DataReader = function(meta, recordType){
5475     
5476     this.meta = meta;
5477     
5478     this.recordType = recordType instanceof Array ? 
5479         Roo.data.Record.create(recordType) : recordType;
5480 };
5481
5482 Roo.data.DataReader.prototype = {
5483      /**
5484      * Create an empty record
5485      * @param {Object} data (optional) - overlay some values
5486      * @return {Roo.data.Record} record created.
5487      */
5488     newRow :  function(d) {
5489         var da =  {};
5490         this.recordType.prototype.fields.each(function(c) {
5491             switch( c.type) {
5492                 case 'int' : da[c.name] = 0; break;
5493                 case 'date' : da[c.name] = new Date(); break;
5494                 case 'float' : da[c.name] = 0.0; break;
5495                 case 'boolean' : da[c.name] = false; break;
5496                 default : da[c.name] = ""; break;
5497             }
5498             
5499         });
5500         return new this.recordType(Roo.apply(da, d));
5501     }
5502     
5503 };/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514 /**
5515  * @class Roo.data.DataProxy
5516  * @extends Roo.data.Observable
5517  * This class is an abstract base class for implementations which provide retrieval of
5518  * unformatted data objects.<br>
5519  * <p>
5520  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5521  * (of the appropriate type which knows how to parse the data object) to provide a block of
5522  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5523  * <p>
5524  * Custom implementations must implement the load method as described in
5525  * {@link Roo.data.HttpProxy#load}.
5526  */
5527 Roo.data.DataProxy = function(){
5528     this.addEvents({
5529         /**
5530          * @event beforeload
5531          * Fires before a network request is made to retrieve a data object.
5532          * @param {Object} This DataProxy object.
5533          * @param {Object} params The params parameter to the load function.
5534          */
5535         beforeload : true,
5536         /**
5537          * @event load
5538          * Fires before the load method's callback is called.
5539          * @param {Object} This DataProxy object.
5540          * @param {Object} o The data object.
5541          * @param {Object} arg The callback argument object passed to the load function.
5542          */
5543         load : true,
5544         /**
5545          * @event loadexception
5546          * Fires if an Exception occurs during data retrieval.
5547          * @param {Object} This DataProxy object.
5548          * @param {Object} o The data object.
5549          * @param {Object} arg The callback argument object passed to the load function.
5550          * @param {Object} e The Exception.
5551          */
5552         loadexception : true
5553     });
5554     Roo.data.DataProxy.superclass.constructor.call(this);
5555 };
5556
5557 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5558
5559     /**
5560      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5561      */
5562 /*
5563  * Based on:
5564  * Ext JS Library 1.1.1
5565  * Copyright(c) 2006-2007, Ext JS, LLC.
5566  *
5567  * Originally Released Under LGPL - original licence link has changed is not relivant.
5568  *
5569  * Fork - LGPL
5570  * <script type="text/javascript">
5571  */
5572 /**
5573  * @class Roo.data.MemoryProxy
5574  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5575  * to the Reader when its load method is called.
5576  * @constructor
5577  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5578  */
5579 Roo.data.MemoryProxy = function(data){
5580     if (data.data) {
5581         data = data.data;
5582     }
5583     Roo.data.MemoryProxy.superclass.constructor.call(this);
5584     this.data = data;
5585 };
5586
5587 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5588     /**
5589      * Load data from the requested source (in this case an in-memory
5590      * data object passed to the constructor), read the data object into
5591      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5592      * process that block using the passed callback.
5593      * @param {Object} params This parameter is not used by the MemoryProxy class.
5594      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5595      * object into a block of Roo.data.Records.
5596      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5597      * The function must be passed <ul>
5598      * <li>The Record block object</li>
5599      * <li>The "arg" argument from the load function</li>
5600      * <li>A boolean success indicator</li>
5601      * </ul>
5602      * @param {Object} scope The scope in which to call the callback
5603      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5604      */
5605     load : function(params, reader, callback, scope, arg){
5606         params = params || {};
5607         var result;
5608         try {
5609             result = reader.readRecords(this.data);
5610         }catch(e){
5611             this.fireEvent("loadexception", this, arg, null, e);
5612             callback.call(scope, null, arg, false);
5613             return;
5614         }
5615         callback.call(scope, result, arg, true);
5616     },
5617     
5618     // private
5619     update : function(params, records){
5620         
5621     }
5622 });/*
5623  * Based on:
5624  * Ext JS Library 1.1.1
5625  * Copyright(c) 2006-2007, Ext JS, LLC.
5626  *
5627  * Originally Released Under LGPL - original licence link has changed is not relivant.
5628  *
5629  * Fork - LGPL
5630  * <script type="text/javascript">
5631  */
5632 /**
5633  * @class Roo.data.HttpProxy
5634  * @extends Roo.data.DataProxy
5635  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5636  * configured to reference a certain URL.<br><br>
5637  * <p>
5638  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5639  * from which the running page was served.<br><br>
5640  * <p>
5641  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5642  * <p>
5643  * Be aware that to enable the browser to parse an XML document, the server must set
5644  * the Content-Type header in the HTTP response to "text/xml".
5645  * @constructor
5646  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5647  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5648  * will be used to make the request.
5649  */
5650 Roo.data.HttpProxy = function(conn){
5651     Roo.data.HttpProxy.superclass.constructor.call(this);
5652     // is conn a conn config or a real conn?
5653     this.conn = conn;
5654     this.useAjax = !conn || !conn.events;
5655   
5656 };
5657
5658 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5659     // thse are take from connection...
5660     
5661     /**
5662      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5663      */
5664     /**
5665      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5666      * extra parameters to each request made by this object. (defaults to undefined)
5667      */
5668     /**
5669      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5670      *  to each request made by this object. (defaults to undefined)
5671      */
5672     /**
5673      * @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)
5674      */
5675     /**
5676      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5677      */
5678      /**
5679      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5680      * @type Boolean
5681      */
5682   
5683
5684     /**
5685      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5686      * @type Boolean
5687      */
5688     /**
5689      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5690      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5691      * a finer-grained basis than the DataProxy events.
5692      */
5693     getConnection : function(){
5694         return this.useAjax ? Roo.Ajax : this.conn;
5695     },
5696
5697     /**
5698      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5699      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5700      * process that block using the passed callback.
5701      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5702      * for the request to the remote server.
5703      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5704      * object into a block of Roo.data.Records.
5705      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5706      * The function must be passed <ul>
5707      * <li>The Record block object</li>
5708      * <li>The "arg" argument from the load function</li>
5709      * <li>A boolean success indicator</li>
5710      * </ul>
5711      * @param {Object} scope The scope in which to call the callback
5712      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5713      */
5714     load : function(params, reader, callback, scope, arg){
5715         if(this.fireEvent("beforeload", this, params) !== false){
5716             var  o = {
5717                 params : params || {},
5718                 request: {
5719                     callback : callback,
5720                     scope : scope,
5721                     arg : arg
5722                 },
5723                 reader: reader,
5724                 callback : this.loadResponse,
5725                 scope: this
5726             };
5727             if(this.useAjax){
5728                 Roo.applyIf(o, this.conn);
5729                 if(this.activeRequest){
5730                     Roo.Ajax.abort(this.activeRequest);
5731                 }
5732                 this.activeRequest = Roo.Ajax.request(o);
5733             }else{
5734                 this.conn.request(o);
5735             }
5736         }else{
5737             callback.call(scope||this, null, arg, false);
5738         }
5739     },
5740
5741     // private
5742     loadResponse : function(o, success, response){
5743         delete this.activeRequest;
5744         if(!success){
5745             this.fireEvent("loadexception", this, o, response);
5746             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5747             return;
5748         }
5749         var result;
5750         try {
5751             result = o.reader.read(response);
5752         }catch(e){
5753             this.fireEvent("loadexception", this, o, response, e);
5754             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5755             return;
5756         }
5757         
5758         this.fireEvent("load", this, o, o.request.arg);
5759         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5760     },
5761
5762     // private
5763     update : function(dataSet){
5764
5765     },
5766
5767     // private
5768     updateResponse : function(dataSet){
5769
5770     }
5771 });/*
5772  * Based on:
5773  * Ext JS Library 1.1.1
5774  * Copyright(c) 2006-2007, Ext JS, LLC.
5775  *
5776  * Originally Released Under LGPL - original licence link has changed is not relivant.
5777  *
5778  * Fork - LGPL
5779  * <script type="text/javascript">
5780  */
5781
5782 /**
5783  * @class Roo.data.ScriptTagProxy
5784  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5785  * other than the originating domain of the running page.<br><br>
5786  * <p>
5787  * <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
5788  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5789  * <p>
5790  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5791  * source code that is used as the source inside a &lt;script> tag.<br><br>
5792  * <p>
5793  * In order for the browser to process the returned data, the server must wrap the data object
5794  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5795  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5796  * depending on whether the callback name was passed:
5797  * <p>
5798  * <pre><code>
5799 boolean scriptTag = false;
5800 String cb = request.getParameter("callback");
5801 if (cb != null) {
5802     scriptTag = true;
5803     response.setContentType("text/javascript");
5804 } else {
5805     response.setContentType("application/x-json");
5806 }
5807 Writer out = response.getWriter();
5808 if (scriptTag) {
5809     out.write(cb + "(");
5810 }
5811 out.print(dataBlock.toJsonString());
5812 if (scriptTag) {
5813     out.write(");");
5814 }
5815 </pre></code>
5816  *
5817  * @constructor
5818  * @param {Object} config A configuration object.
5819  */
5820 Roo.data.ScriptTagProxy = function(config){
5821     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5822     Roo.apply(this, config);
5823     this.head = document.getElementsByTagName("head")[0];
5824 };
5825
5826 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5827
5828 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5829     /**
5830      * @cfg {String} url The URL from which to request the data object.
5831      */
5832     /**
5833      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5834      */
5835     timeout : 30000,
5836     /**
5837      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5838      * the server the name of the callback function set up by the load call to process the returned data object.
5839      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5840      * javascript output which calls this named function passing the data object as its only parameter.
5841      */
5842     callbackParam : "callback",
5843     /**
5844      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5845      * name to the request.
5846      */
5847     nocache : true,
5848
5849     /**
5850      * Load data from the configured URL, read the data object into
5851      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5852      * process that block using the passed callback.
5853      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5854      * for the request to the remote server.
5855      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5856      * object into a block of Roo.data.Records.
5857      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5858      * The function must be passed <ul>
5859      * <li>The Record block object</li>
5860      * <li>The "arg" argument from the load function</li>
5861      * <li>A boolean success indicator</li>
5862      * </ul>
5863      * @param {Object} scope The scope in which to call the callback
5864      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5865      */
5866     load : function(params, reader, callback, scope, arg){
5867         if(this.fireEvent("beforeload", this, params) !== false){
5868
5869             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5870
5871             var url = this.url;
5872             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5873             if(this.nocache){
5874                 url += "&_dc=" + (new Date().getTime());
5875             }
5876             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5877             var trans = {
5878                 id : transId,
5879                 cb : "stcCallback"+transId,
5880                 scriptId : "stcScript"+transId,
5881                 params : params,
5882                 arg : arg,
5883                 url : url,
5884                 callback : callback,
5885                 scope : scope,
5886                 reader : reader
5887             };
5888             var conn = this;
5889
5890             window[trans.cb] = function(o){
5891                 conn.handleResponse(o, trans);
5892             };
5893
5894             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5895
5896             if(this.autoAbort !== false){
5897                 this.abort();
5898             }
5899
5900             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5901
5902             var script = document.createElement("script");
5903             script.setAttribute("src", url);
5904             script.setAttribute("type", "text/javascript");
5905             script.setAttribute("id", trans.scriptId);
5906             this.head.appendChild(script);
5907
5908             this.trans = trans;
5909         }else{
5910             callback.call(scope||this, null, arg, false);
5911         }
5912     },
5913
5914     // private
5915     isLoading : function(){
5916         return this.trans ? true : false;
5917     },
5918
5919     /**
5920      * Abort the current server request.
5921      */
5922     abort : function(){
5923         if(this.isLoading()){
5924             this.destroyTrans(this.trans);
5925         }
5926     },
5927
5928     // private
5929     destroyTrans : function(trans, isLoaded){
5930         this.head.removeChild(document.getElementById(trans.scriptId));
5931         clearTimeout(trans.timeoutId);
5932         if(isLoaded){
5933             window[trans.cb] = undefined;
5934             try{
5935                 delete window[trans.cb];
5936             }catch(e){}
5937         }else{
5938             // if hasn't been loaded, wait for load to remove it to prevent script error
5939             window[trans.cb] = function(){
5940                 window[trans.cb] = undefined;
5941                 try{
5942                     delete window[trans.cb];
5943                 }catch(e){}
5944             };
5945         }
5946     },
5947
5948     // private
5949     handleResponse : function(o, trans){
5950         this.trans = false;
5951         this.destroyTrans(trans, true);
5952         var result;
5953         try {
5954             result = trans.reader.readRecords(o);
5955         }catch(e){
5956             this.fireEvent("loadexception", this, o, trans.arg, e);
5957             trans.callback.call(trans.scope||window, null, trans.arg, false);
5958             return;
5959         }
5960         this.fireEvent("load", this, o, trans.arg);
5961         trans.callback.call(trans.scope||window, result, trans.arg, true);
5962     },
5963
5964     // private
5965     handleFailure : function(trans){
5966         this.trans = false;
5967         this.destroyTrans(trans, false);
5968         this.fireEvent("loadexception", this, null, trans.arg);
5969         trans.callback.call(trans.scope||window, null, trans.arg, false);
5970     }
5971 });/*
5972  * Based on:
5973  * Ext JS Library 1.1.1
5974  * Copyright(c) 2006-2007, Ext JS, LLC.
5975  *
5976  * Originally Released Under LGPL - original licence link has changed is not relivant.
5977  *
5978  * Fork - LGPL
5979  * <script type="text/javascript">
5980  */
5981
5982 /**
5983  * @class Roo.data.JsonReader
5984  * @extends Roo.data.DataReader
5985  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
5986  * based on mappings in a provided Roo.data.Record constructor.
5987  * 
5988  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
5989  * in the reply previously. 
5990  * 
5991  * <p>
5992  * Example code:
5993  * <pre><code>
5994 var RecordDef = Roo.data.Record.create([
5995     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
5996     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
5997 ]);
5998 var myReader = new Roo.data.JsonReader({
5999     totalProperty: "results",    // The property which contains the total dataset size (optional)
6000     root: "rows",                // The property which contains an Array of row objects
6001     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6002 }, RecordDef);
6003 </code></pre>
6004  * <p>
6005  * This would consume a JSON file like this:
6006  * <pre><code>
6007 { 'results': 2, 'rows': [
6008     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6009     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6010 }
6011 </code></pre>
6012  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6013  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6014  * paged from the remote server.
6015  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6016  * @cfg {String} root name of the property which contains the Array of row objects.
6017  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6018  * @constructor
6019  * Create a new JsonReader
6020  * @param {Object} meta Metadata configuration options
6021  * @param {Object} recordType Either an Array of field definition objects,
6022  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6023  */
6024 Roo.data.JsonReader = function(meta, recordType){
6025     
6026     meta = meta || {};
6027     // set some defaults:
6028     Roo.applyIf(meta, {
6029         totalProperty: 'total',
6030         successProperty : 'success',
6031         root : 'data',
6032         id : 'id'
6033     });
6034     
6035     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6036 };
6037 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6038     
6039     /**
6040      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6041      * Used by Store query builder to append _requestMeta to params.
6042      * 
6043      */
6044     metaFromRemote : false,
6045     /**
6046      * This method is only used by a DataProxy which has retrieved data from a remote server.
6047      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6048      * @return {Object} data A data block which is used by an Roo.data.Store object as
6049      * a cache of Roo.data.Records.
6050      */
6051     read : function(response){
6052         var json = response.responseText;
6053        
6054         var o = /* eval:var:o */ eval("("+json+")");
6055         if(!o) {
6056             throw {message: "JsonReader.read: Json object not found"};
6057         }
6058         
6059         if(o.metaData){
6060             
6061             delete this.ef;
6062             this.metaFromRemote = true;
6063             this.meta = o.metaData;
6064             this.recordType = Roo.data.Record.create(o.metaData.fields);
6065             this.onMetaChange(this.meta, this.recordType, o);
6066         }
6067         return this.readRecords(o);
6068     },
6069
6070     // private function a store will implement
6071     onMetaChange : function(meta, recordType, o){
6072
6073     },
6074
6075     /**
6076          * @ignore
6077          */
6078     simpleAccess: function(obj, subsc) {
6079         return obj[subsc];
6080     },
6081
6082         /**
6083          * @ignore
6084          */
6085     getJsonAccessor: function(){
6086         var re = /[\[\.]/;
6087         return function(expr) {
6088             try {
6089                 return(re.test(expr))
6090                     ? new Function("obj", "return obj." + expr)
6091                     : function(obj){
6092                         return obj[expr];
6093                     };
6094             } catch(e){}
6095             return Roo.emptyFn;
6096         };
6097     }(),
6098
6099     /**
6100      * Create a data block containing Roo.data.Records from an XML document.
6101      * @param {Object} o An object which contains an Array of row objects in the property specified
6102      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6103      * which contains the total size of the dataset.
6104      * @return {Object} data A data block which is used by an Roo.data.Store object as
6105      * a cache of Roo.data.Records.
6106      */
6107     readRecords : function(o){
6108         /**
6109          * After any data loads, the raw JSON data is available for further custom processing.
6110          * @type Object
6111          */
6112         this.jsonData = o;
6113         var s = this.meta, Record = this.recordType,
6114             f = Record.prototype.fields, fi = f.items, fl = f.length;
6115
6116 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6117         if (!this.ef) {
6118             if(s.totalProperty) {
6119                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6120                 }
6121                 if(s.successProperty) {
6122                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6123                 }
6124                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6125                 if (s.id) {
6126                         var g = this.getJsonAccessor(s.id);
6127                         this.getId = function(rec) {
6128                                 var r = g(rec);
6129                                 return (r === undefined || r === "") ? null : r;
6130                         };
6131                 } else {
6132                         this.getId = function(){return null;};
6133                 }
6134             this.ef = [];
6135             for(var jj = 0; jj < fl; jj++){
6136                 f = fi[jj];
6137                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6138                 this.ef[jj] = this.getJsonAccessor(map);
6139             }
6140         }
6141
6142         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6143         if(s.totalProperty){
6144             var vt = parseInt(this.getTotal(o), 10);
6145             if(!isNaN(vt)){
6146                 totalRecords = vt;
6147             }
6148         }
6149         if(s.successProperty){
6150             var vs = this.getSuccess(o);
6151             if(vs === false || vs === 'false'){
6152                 success = false;
6153             }
6154         }
6155         var records = [];
6156             for(var i = 0; i < c; i++){
6157                     var n = root[i];
6158                 var values = {};
6159                 var id = this.getId(n);
6160                 for(var j = 0; j < fl; j++){
6161                     f = fi[j];
6162                 var v = this.ef[j](n);
6163                 if (!f.convert) {
6164                     Roo.log('missing convert for ' + f.name);
6165                     Roo.log(f);
6166                     continue;
6167                 }
6168                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6169                 }
6170                 var record = new Record(values, id);
6171                 record.json = n;
6172                 records[i] = record;
6173             }
6174             return {
6175                 success : success,
6176                 records : records,
6177                 totalRecords : totalRecords
6178             };
6179     }
6180 });/*
6181  * Based on:
6182  * Ext JS Library 1.1.1
6183  * Copyright(c) 2006-2007, Ext JS, LLC.
6184  *
6185  * Originally Released Under LGPL - original licence link has changed is not relivant.
6186  *
6187  * Fork - LGPL
6188  * <script type="text/javascript">
6189  */
6190
6191 /**
6192  * @class Roo.data.XmlReader
6193  * @extends Roo.data.DataReader
6194  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6195  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6196  * <p>
6197  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6198  * header in the HTTP response must be set to "text/xml".</em>
6199  * <p>
6200  * Example code:
6201  * <pre><code>
6202 var RecordDef = Roo.data.Record.create([
6203    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6204    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6205 ]);
6206 var myReader = new Roo.data.XmlReader({
6207    totalRecords: "results", // The element which contains the total dataset size (optional)
6208    record: "row",           // The repeated element which contains row information
6209    id: "id"                 // The element within the row that provides an ID for the record (optional)
6210 }, RecordDef);
6211 </code></pre>
6212  * <p>
6213  * This would consume an XML file like this:
6214  * <pre><code>
6215 &lt;?xml?>
6216 &lt;dataset>
6217  &lt;results>2&lt;/results>
6218  &lt;row>
6219    &lt;id>1&lt;/id>
6220    &lt;name>Bill&lt;/name>
6221    &lt;occupation>Gardener&lt;/occupation>
6222  &lt;/row>
6223  &lt;row>
6224    &lt;id>2&lt;/id>
6225    &lt;name>Ben&lt;/name>
6226    &lt;occupation>Horticulturalist&lt;/occupation>
6227  &lt;/row>
6228 &lt;/dataset>
6229 </code></pre>
6230  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6231  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6232  * paged from the remote server.
6233  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6234  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6235  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6236  * a record identifier value.
6237  * @constructor
6238  * Create a new XmlReader
6239  * @param {Object} meta Metadata configuration options
6240  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6241  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6242  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6243  */
6244 Roo.data.XmlReader = function(meta, recordType){
6245     meta = meta || {};
6246     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6247 };
6248 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6249     /**
6250      * This method is only used by a DataProxy which has retrieved data from a remote server.
6251          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6252          * to contain a method called 'responseXML' that returns an XML document object.
6253      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6254      * a cache of Roo.data.Records.
6255      */
6256     read : function(response){
6257         var doc = response.responseXML;
6258         if(!doc) {
6259             throw {message: "XmlReader.read: XML Document not available"};
6260         }
6261         return this.readRecords(doc);
6262     },
6263
6264     /**
6265      * Create a data block containing Roo.data.Records from an XML document.
6266          * @param {Object} doc A parsed XML document.
6267      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6268      * a cache of Roo.data.Records.
6269      */
6270     readRecords : function(doc){
6271         /**
6272          * After any data loads/reads, the raw XML Document is available for further custom processing.
6273          * @type XMLDocument
6274          */
6275         this.xmlData = doc;
6276         var root = doc.documentElement || doc;
6277         var q = Roo.DomQuery;
6278         var recordType = this.recordType, fields = recordType.prototype.fields;
6279         var sid = this.meta.id;
6280         var totalRecords = 0, success = true;
6281         if(this.meta.totalRecords){
6282             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6283         }
6284         
6285         if(this.meta.success){
6286             var sv = q.selectValue(this.meta.success, root, true);
6287             success = sv !== false && sv !== 'false';
6288         }
6289         var records = [];
6290         var ns = q.select(this.meta.record, root);
6291         for(var i = 0, len = ns.length; i < len; i++) {
6292                 var n = ns[i];
6293                 var values = {};
6294                 var id = sid ? q.selectValue(sid, n) : undefined;
6295                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6296                     var f = fields.items[j];
6297                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6298                     v = f.convert(v);
6299                     values[f.name] = v;
6300                 }
6301                 var record = new recordType(values, id);
6302                 record.node = n;
6303                 records[records.length] = record;
6304             }
6305
6306             return {
6307                 success : success,
6308                 records : records,
6309                 totalRecords : totalRecords || records.length
6310             };
6311     }
6312 });/*
6313  * Based on:
6314  * Ext JS Library 1.1.1
6315  * Copyright(c) 2006-2007, Ext JS, LLC.
6316  *
6317  * Originally Released Under LGPL - original licence link has changed is not relivant.
6318  *
6319  * Fork - LGPL
6320  * <script type="text/javascript">
6321  */
6322
6323 /**
6324  * @class Roo.data.ArrayReader
6325  * @extends Roo.data.DataReader
6326  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6327  * Each element of that Array represents a row of data fields. The
6328  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6329  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6330  * <p>
6331  * Example code:.
6332  * <pre><code>
6333 var RecordDef = Roo.data.Record.create([
6334     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6335     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6336 ]);
6337 var myReader = new Roo.data.ArrayReader({
6338     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6339 }, RecordDef);
6340 </code></pre>
6341  * <p>
6342  * This would consume an Array like this:
6343  * <pre><code>
6344 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6345   </code></pre>
6346  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6347  * @constructor
6348  * Create a new JsonReader
6349  * @param {Object} meta Metadata configuration options.
6350  * @param {Object} recordType Either an Array of field definition objects
6351  * as specified to {@link Roo.data.Record#create},
6352  * or an {@link Roo.data.Record} object
6353  * created using {@link Roo.data.Record#create}.
6354  */
6355 Roo.data.ArrayReader = function(meta, recordType){
6356     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6357 };
6358
6359 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6360     /**
6361      * Create a data block containing Roo.data.Records from an XML document.
6362      * @param {Object} o An Array of row objects which represents the dataset.
6363      * @return {Object} data A data block which is used by an Roo.data.Store object as
6364      * a cache of Roo.data.Records.
6365      */
6366     readRecords : function(o){
6367         var sid = this.meta ? this.meta.id : null;
6368         var recordType = this.recordType, fields = recordType.prototype.fields;
6369         var records = [];
6370         var root = o;
6371             for(var i = 0; i < root.length; i++){
6372                     var n = root[i];
6373                 var values = {};
6374                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6375                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6376                 var f = fields.items[j];
6377                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6378                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6379                 v = f.convert(v);
6380                 values[f.name] = v;
6381             }
6382                 var record = new recordType(values, id);
6383                 record.json = n;
6384                 records[records.length] = record;
6385             }
6386             return {
6387                 records : records,
6388                 totalRecords : records.length
6389             };
6390     }
6391 });/*
6392  * Based on:
6393  * Ext JS Library 1.1.1
6394  * Copyright(c) 2006-2007, Ext JS, LLC.
6395  *
6396  * Originally Released Under LGPL - original licence link has changed is not relivant.
6397  *
6398  * Fork - LGPL
6399  * <script type="text/javascript">
6400  */
6401
6402
6403 /**
6404  * @class Roo.data.Tree
6405  * @extends Roo.util.Observable
6406  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6407  * in the tree have most standard DOM functionality.
6408  * @constructor
6409  * @param {Node} root (optional) The root node
6410  */
6411 Roo.data.Tree = function(root){
6412    this.nodeHash = {};
6413    /**
6414     * The root node for this tree
6415     * @type Node
6416     */
6417    this.root = null;
6418    if(root){
6419        this.setRootNode(root);
6420    }
6421    this.addEvents({
6422        /**
6423         * @event append
6424         * Fires when a new child node is appended to a node in this tree.
6425         * @param {Tree} tree The owner tree
6426         * @param {Node} parent The parent node
6427         * @param {Node} node The newly appended node
6428         * @param {Number} index The index of the newly appended node
6429         */
6430        "append" : true,
6431        /**
6432         * @event remove
6433         * Fires when a child node is removed from a node in this tree.
6434         * @param {Tree} tree The owner tree
6435         * @param {Node} parent The parent node
6436         * @param {Node} node The child node removed
6437         */
6438        "remove" : true,
6439        /**
6440         * @event move
6441         * Fires when a node is moved to a new location in the tree
6442         * @param {Tree} tree The owner tree
6443         * @param {Node} node The node moved
6444         * @param {Node} oldParent The old parent of this node
6445         * @param {Node} newParent The new parent of this node
6446         * @param {Number} index The index it was moved to
6447         */
6448        "move" : true,
6449        /**
6450         * @event insert
6451         * Fires when a new child node is inserted in a node in this tree.
6452         * @param {Tree} tree The owner tree
6453         * @param {Node} parent The parent node
6454         * @param {Node} node The child node inserted
6455         * @param {Node} refNode The child node the node was inserted before
6456         */
6457        "insert" : true,
6458        /**
6459         * @event beforeappend
6460         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6461         * @param {Tree} tree The owner tree
6462         * @param {Node} parent The parent node
6463         * @param {Node} node The child node to be appended
6464         */
6465        "beforeappend" : true,
6466        /**
6467         * @event beforeremove
6468         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6469         * @param {Tree} tree The owner tree
6470         * @param {Node} parent The parent node
6471         * @param {Node} node The child node to be removed
6472         */
6473        "beforeremove" : true,
6474        /**
6475         * @event beforemove
6476         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6477         * @param {Tree} tree The owner tree
6478         * @param {Node} node The node being moved
6479         * @param {Node} oldParent The parent of the node
6480         * @param {Node} newParent The new parent the node is moving to
6481         * @param {Number} index The index it is being moved to
6482         */
6483        "beforemove" : true,
6484        /**
6485         * @event beforeinsert
6486         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6487         * @param {Tree} tree The owner tree
6488         * @param {Node} parent The parent node
6489         * @param {Node} node The child node to be inserted
6490         * @param {Node} refNode The child node the node is being inserted before
6491         */
6492        "beforeinsert" : true
6493    });
6494
6495     Roo.data.Tree.superclass.constructor.call(this);
6496 };
6497
6498 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6499     pathSeparator: "/",
6500
6501     proxyNodeEvent : function(){
6502         return this.fireEvent.apply(this, arguments);
6503     },
6504
6505     /**
6506      * Returns the root node for this tree.
6507      * @return {Node}
6508      */
6509     getRootNode : function(){
6510         return this.root;
6511     },
6512
6513     /**
6514      * Sets the root node for this tree.
6515      * @param {Node} node
6516      * @return {Node}
6517      */
6518     setRootNode : function(node){
6519         this.root = node;
6520         node.ownerTree = this;
6521         node.isRoot = true;
6522         this.registerNode(node);
6523         return node;
6524     },
6525
6526     /**
6527      * Gets a node in this tree by its id.
6528      * @param {String} id
6529      * @return {Node}
6530      */
6531     getNodeById : function(id){
6532         return this.nodeHash[id];
6533     },
6534
6535     registerNode : function(node){
6536         this.nodeHash[node.id] = node;
6537     },
6538
6539     unregisterNode : function(node){
6540         delete this.nodeHash[node.id];
6541     },
6542
6543     toString : function(){
6544         return "[Tree"+(this.id?" "+this.id:"")+"]";
6545     }
6546 });
6547
6548 /**
6549  * @class Roo.data.Node
6550  * @extends Roo.util.Observable
6551  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6552  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6553  * @constructor
6554  * @param {Object} attributes The attributes/config for the node
6555  */
6556 Roo.data.Node = function(attributes){
6557     /**
6558      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6559      * @type {Object}
6560      */
6561     this.attributes = attributes || {};
6562     this.leaf = this.attributes.leaf;
6563     /**
6564      * The node id. @type String
6565      */
6566     this.id = this.attributes.id;
6567     if(!this.id){
6568         this.id = Roo.id(null, "ynode-");
6569         this.attributes.id = this.id;
6570     }
6571     /**
6572      * All child nodes of this node. @type Array
6573      */
6574     this.childNodes = [];
6575     if(!this.childNodes.indexOf){ // indexOf is a must
6576         this.childNodes.indexOf = function(o){
6577             for(var i = 0, len = this.length; i < len; i++){
6578                 if(this[i] == o) {
6579                     return i;
6580                 }
6581             }
6582             return -1;
6583         };
6584     }
6585     /**
6586      * The parent node for this node. @type Node
6587      */
6588     this.parentNode = null;
6589     /**
6590      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6591      */
6592     this.firstChild = null;
6593     /**
6594      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6595      */
6596     this.lastChild = null;
6597     /**
6598      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6599      */
6600     this.previousSibling = null;
6601     /**
6602      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6603      */
6604     this.nextSibling = null;
6605
6606     this.addEvents({
6607        /**
6608         * @event append
6609         * Fires when a new child node is appended
6610         * @param {Tree} tree The owner tree
6611         * @param {Node} this This node
6612         * @param {Node} node The newly appended node
6613         * @param {Number} index The index of the newly appended node
6614         */
6615        "append" : true,
6616        /**
6617         * @event remove
6618         * Fires when a child node is removed
6619         * @param {Tree} tree The owner tree
6620         * @param {Node} this This node
6621         * @param {Node} node The removed node
6622         */
6623        "remove" : true,
6624        /**
6625         * @event move
6626         * Fires when this node is moved to a new location in the tree
6627         * @param {Tree} tree The owner tree
6628         * @param {Node} this This node
6629         * @param {Node} oldParent The old parent of this node
6630         * @param {Node} newParent The new parent of this node
6631         * @param {Number} index The index it was moved to
6632         */
6633        "move" : true,
6634        /**
6635         * @event insert
6636         * Fires when a new child node is inserted.
6637         * @param {Tree} tree The owner tree
6638         * @param {Node} this This node
6639         * @param {Node} node The child node inserted
6640         * @param {Node} refNode The child node the node was inserted before
6641         */
6642        "insert" : true,
6643        /**
6644         * @event beforeappend
6645         * Fires before a new child is appended, return false to cancel the append.
6646         * @param {Tree} tree The owner tree
6647         * @param {Node} this This node
6648         * @param {Node} node The child node to be appended
6649         */
6650        "beforeappend" : true,
6651        /**
6652         * @event beforeremove
6653         * Fires before a child is removed, return false to cancel the remove.
6654         * @param {Tree} tree The owner tree
6655         * @param {Node} this This node
6656         * @param {Node} node The child node to be removed
6657         */
6658        "beforeremove" : true,
6659        /**
6660         * @event beforemove
6661         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6662         * @param {Tree} tree The owner tree
6663         * @param {Node} this This node
6664         * @param {Node} oldParent The parent of this node
6665         * @param {Node} newParent The new parent this node is moving to
6666         * @param {Number} index The index it is being moved to
6667         */
6668        "beforemove" : true,
6669        /**
6670         * @event beforeinsert
6671         * Fires before a new child is inserted, return false to cancel the insert.
6672         * @param {Tree} tree The owner tree
6673         * @param {Node} this This node
6674         * @param {Node} node The child node to be inserted
6675         * @param {Node} refNode The child node the node is being inserted before
6676         */
6677        "beforeinsert" : true
6678    });
6679     this.listeners = this.attributes.listeners;
6680     Roo.data.Node.superclass.constructor.call(this);
6681 };
6682
6683 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6684     fireEvent : function(evtName){
6685         // first do standard event for this node
6686         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6687             return false;
6688         }
6689         // then bubble it up to the tree if the event wasn't cancelled
6690         var ot = this.getOwnerTree();
6691         if(ot){
6692             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6693                 return false;
6694             }
6695         }
6696         return true;
6697     },
6698
6699     /**
6700      * Returns true if this node is a leaf
6701      * @return {Boolean}
6702      */
6703     isLeaf : function(){
6704         return this.leaf === true;
6705     },
6706
6707     // private
6708     setFirstChild : function(node){
6709         this.firstChild = node;
6710     },
6711
6712     //private
6713     setLastChild : function(node){
6714         this.lastChild = node;
6715     },
6716
6717
6718     /**
6719      * Returns true if this node is the last child of its parent
6720      * @return {Boolean}
6721      */
6722     isLast : function(){
6723        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6724     },
6725
6726     /**
6727      * Returns true if this node is the first child of its parent
6728      * @return {Boolean}
6729      */
6730     isFirst : function(){
6731        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6732     },
6733
6734     hasChildNodes : function(){
6735         return !this.isLeaf() && this.childNodes.length > 0;
6736     },
6737
6738     /**
6739      * Insert node(s) as the last child node of this node.
6740      * @param {Node/Array} node The node or Array of nodes to append
6741      * @return {Node} The appended node if single append, or null if an array was passed
6742      */
6743     appendChild : function(node){
6744         var multi = false;
6745         if(node instanceof Array){
6746             multi = node;
6747         }else if(arguments.length > 1){
6748             multi = arguments;
6749         }
6750         // if passed an array or multiple args do them one by one
6751         if(multi){
6752             for(var i = 0, len = multi.length; i < len; i++) {
6753                 this.appendChild(multi[i]);
6754             }
6755         }else{
6756             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6757                 return false;
6758             }
6759             var index = this.childNodes.length;
6760             var oldParent = node.parentNode;
6761             // it's a move, make sure we move it cleanly
6762             if(oldParent){
6763                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6764                     return false;
6765                 }
6766                 oldParent.removeChild(node);
6767             }
6768             index = this.childNodes.length;
6769             if(index == 0){
6770                 this.setFirstChild(node);
6771             }
6772             this.childNodes.push(node);
6773             node.parentNode = this;
6774             var ps = this.childNodes[index-1];
6775             if(ps){
6776                 node.previousSibling = ps;
6777                 ps.nextSibling = node;
6778             }else{
6779                 node.previousSibling = null;
6780             }
6781             node.nextSibling = null;
6782             this.setLastChild(node);
6783             node.setOwnerTree(this.getOwnerTree());
6784             this.fireEvent("append", this.ownerTree, this, node, index);
6785             if(oldParent){
6786                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6787             }
6788             return node;
6789         }
6790     },
6791
6792     /**
6793      * Removes a child node from this node.
6794      * @param {Node} node The node to remove
6795      * @return {Node} The removed node
6796      */
6797     removeChild : function(node){
6798         var index = this.childNodes.indexOf(node);
6799         if(index == -1){
6800             return false;
6801         }
6802         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6803             return false;
6804         }
6805
6806         // remove it from childNodes collection
6807         this.childNodes.splice(index, 1);
6808
6809         // update siblings
6810         if(node.previousSibling){
6811             node.previousSibling.nextSibling = node.nextSibling;
6812         }
6813         if(node.nextSibling){
6814             node.nextSibling.previousSibling = node.previousSibling;
6815         }
6816
6817         // update child refs
6818         if(this.firstChild == node){
6819             this.setFirstChild(node.nextSibling);
6820         }
6821         if(this.lastChild == node){
6822             this.setLastChild(node.previousSibling);
6823         }
6824
6825         node.setOwnerTree(null);
6826         // clear any references from the node
6827         node.parentNode = null;
6828         node.previousSibling = null;
6829         node.nextSibling = null;
6830         this.fireEvent("remove", this.ownerTree, this, node);
6831         return node;
6832     },
6833
6834     /**
6835      * Inserts the first node before the second node in this nodes childNodes collection.
6836      * @param {Node} node The node to insert
6837      * @param {Node} refNode The node to insert before (if null the node is appended)
6838      * @return {Node} The inserted node
6839      */
6840     insertBefore : function(node, refNode){
6841         if(!refNode){ // like standard Dom, refNode can be null for append
6842             return this.appendChild(node);
6843         }
6844         // nothing to do
6845         if(node == refNode){
6846             return false;
6847         }
6848
6849         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6850             return false;
6851         }
6852         var index = this.childNodes.indexOf(refNode);
6853         var oldParent = node.parentNode;
6854         var refIndex = index;
6855
6856         // when moving internally, indexes will change after remove
6857         if(oldParent == this && this.childNodes.indexOf(node) < index){
6858             refIndex--;
6859         }
6860
6861         // it's a move, make sure we move it cleanly
6862         if(oldParent){
6863             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6864                 return false;
6865             }
6866             oldParent.removeChild(node);
6867         }
6868         if(refIndex == 0){
6869             this.setFirstChild(node);
6870         }
6871         this.childNodes.splice(refIndex, 0, node);
6872         node.parentNode = this;
6873         var ps = this.childNodes[refIndex-1];
6874         if(ps){
6875             node.previousSibling = ps;
6876             ps.nextSibling = node;
6877         }else{
6878             node.previousSibling = null;
6879         }
6880         node.nextSibling = refNode;
6881         refNode.previousSibling = node;
6882         node.setOwnerTree(this.getOwnerTree());
6883         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6884         if(oldParent){
6885             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6886         }
6887         return node;
6888     },
6889
6890     /**
6891      * Returns the child node at the specified index.
6892      * @param {Number} index
6893      * @return {Node}
6894      */
6895     item : function(index){
6896         return this.childNodes[index];
6897     },
6898
6899     /**
6900      * Replaces one child node in this node with another.
6901      * @param {Node} newChild The replacement node
6902      * @param {Node} oldChild The node to replace
6903      * @return {Node} The replaced node
6904      */
6905     replaceChild : function(newChild, oldChild){
6906         this.insertBefore(newChild, oldChild);
6907         this.removeChild(oldChild);
6908         return oldChild;
6909     },
6910
6911     /**
6912      * Returns the index of a child node
6913      * @param {Node} node
6914      * @return {Number} The index of the node or -1 if it was not found
6915      */
6916     indexOf : function(child){
6917         return this.childNodes.indexOf(child);
6918     },
6919
6920     /**
6921      * Returns the tree this node is in.
6922      * @return {Tree}
6923      */
6924     getOwnerTree : function(){
6925         // if it doesn't have one, look for one
6926         if(!this.ownerTree){
6927             var p = this;
6928             while(p){
6929                 if(p.ownerTree){
6930                     this.ownerTree = p.ownerTree;
6931                     break;
6932                 }
6933                 p = p.parentNode;
6934             }
6935         }
6936         return this.ownerTree;
6937     },
6938
6939     /**
6940      * Returns depth of this node (the root node has a depth of 0)
6941      * @return {Number}
6942      */
6943     getDepth : function(){
6944         var depth = 0;
6945         var p = this;
6946         while(p.parentNode){
6947             ++depth;
6948             p = p.parentNode;
6949         }
6950         return depth;
6951     },
6952
6953     // private
6954     setOwnerTree : function(tree){
6955         // if it's move, we need to update everyone
6956         if(tree != this.ownerTree){
6957             if(this.ownerTree){
6958                 this.ownerTree.unregisterNode(this);
6959             }
6960             this.ownerTree = tree;
6961             var cs = this.childNodes;
6962             for(var i = 0, len = cs.length; i < len; i++) {
6963                 cs[i].setOwnerTree(tree);
6964             }
6965             if(tree){
6966                 tree.registerNode(this);
6967             }
6968         }
6969     },
6970
6971     /**
6972      * Returns the path for this node. The path can be used to expand or select this node programmatically.
6973      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
6974      * @return {String} The path
6975      */
6976     getPath : function(attr){
6977         attr = attr || "id";
6978         var p = this.parentNode;
6979         var b = [this.attributes[attr]];
6980         while(p){
6981             b.unshift(p.attributes[attr]);
6982             p = p.parentNode;
6983         }
6984         var sep = this.getOwnerTree().pathSeparator;
6985         return sep + b.join(sep);
6986     },
6987
6988     /**
6989      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
6990      * function call will be the scope provided or the current node. The arguments to the function
6991      * will be the args provided or the current node. If the function returns false at any point,
6992      * the bubble is stopped.
6993      * @param {Function} fn The function to call
6994      * @param {Object} scope (optional) The scope of the function (defaults to current node)
6995      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
6996      */
6997     bubble : function(fn, scope, args){
6998         var p = this;
6999         while(p){
7000             if(fn.call(scope || p, args || p) === false){
7001                 break;
7002             }
7003             p = p.parentNode;
7004         }
7005     },
7006
7007     /**
7008      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7009      * function call will be the scope provided or the current node. The arguments to the function
7010      * will be the args provided or the current node. If the function returns false at any point,
7011      * the cascade is stopped on that branch.
7012      * @param {Function} fn The function to call
7013      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7014      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7015      */
7016     cascade : function(fn, scope, args){
7017         if(fn.call(scope || this, args || this) !== false){
7018             var cs = this.childNodes;
7019             for(var i = 0, len = cs.length; i < len; i++) {
7020                 cs[i].cascade(fn, scope, args);
7021             }
7022         }
7023     },
7024
7025     /**
7026      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7027      * function call will be the scope provided or the current node. The arguments to the function
7028      * will be the args provided or the current node. If the function returns false at any point,
7029      * the iteration stops.
7030      * @param {Function} fn The function to call
7031      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7032      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7033      */
7034     eachChild : function(fn, scope, args){
7035         var cs = this.childNodes;
7036         for(var i = 0, len = cs.length; i < len; i++) {
7037                 if(fn.call(scope || this, args || cs[i]) === false){
7038                     break;
7039                 }
7040         }
7041     },
7042
7043     /**
7044      * Finds the first child that has the attribute with the specified value.
7045      * @param {String} attribute The attribute name
7046      * @param {Mixed} value The value to search for
7047      * @return {Node} The found child or null if none was found
7048      */
7049     findChild : function(attribute, value){
7050         var cs = this.childNodes;
7051         for(var i = 0, len = cs.length; i < len; i++) {
7052                 if(cs[i].attributes[attribute] == value){
7053                     return cs[i];
7054                 }
7055         }
7056         return null;
7057     },
7058
7059     /**
7060      * Finds the first child by a custom function. The child matches if the function passed
7061      * returns true.
7062      * @param {Function} fn
7063      * @param {Object} scope (optional)
7064      * @return {Node} The found child or null if none was found
7065      */
7066     findChildBy : function(fn, scope){
7067         var cs = this.childNodes;
7068         for(var i = 0, len = cs.length; i < len; i++) {
7069                 if(fn.call(scope||cs[i], cs[i]) === true){
7070                     return cs[i];
7071                 }
7072         }
7073         return null;
7074     },
7075
7076     /**
7077      * Sorts this nodes children using the supplied sort function
7078      * @param {Function} fn
7079      * @param {Object} scope (optional)
7080      */
7081     sort : function(fn, scope){
7082         var cs = this.childNodes;
7083         var len = cs.length;
7084         if(len > 0){
7085             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7086             cs.sort(sortFn);
7087             for(var i = 0; i < len; i++){
7088                 var n = cs[i];
7089                 n.previousSibling = cs[i-1];
7090                 n.nextSibling = cs[i+1];
7091                 if(i == 0){
7092                     this.setFirstChild(n);
7093                 }
7094                 if(i == len-1){
7095                     this.setLastChild(n);
7096                 }
7097             }
7098         }
7099     },
7100
7101     /**
7102      * Returns true if this node is an ancestor (at any point) of the passed node.
7103      * @param {Node} node
7104      * @return {Boolean}
7105      */
7106     contains : function(node){
7107         return node.isAncestor(this);
7108     },
7109
7110     /**
7111      * Returns true if the passed node is an ancestor (at any point) of this node.
7112      * @param {Node} node
7113      * @return {Boolean}
7114      */
7115     isAncestor : function(node){
7116         var p = this.parentNode;
7117         while(p){
7118             if(p == node){
7119                 return true;
7120             }
7121             p = p.parentNode;
7122         }
7123         return false;
7124     },
7125
7126     toString : function(){
7127         return "[Node"+(this.id?" "+this.id:"")+"]";
7128     }
7129 });/*
7130  * Based on:
7131  * Ext JS Library 1.1.1
7132  * Copyright(c) 2006-2007, Ext JS, LLC.
7133  *
7134  * Originally Released Under LGPL - original licence link has changed is not relivant.
7135  *
7136  * Fork - LGPL
7137  * <script type="text/javascript">
7138  */
7139  
7140
7141 /**
7142  * @class Roo.ComponentMgr
7143  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7144  * @singleton
7145  */
7146 Roo.ComponentMgr = function(){
7147     var all = new Roo.util.MixedCollection();
7148
7149     return {
7150         /**
7151          * Registers a component.
7152          * @param {Roo.Component} c The component
7153          */
7154         register : function(c){
7155             all.add(c);
7156         },
7157
7158         /**
7159          * Unregisters a component.
7160          * @param {Roo.Component} c The component
7161          */
7162         unregister : function(c){
7163             all.remove(c);
7164         },
7165
7166         /**
7167          * Returns a component by id
7168          * @param {String} id The component id
7169          */
7170         get : function(id){
7171             return all.get(id);
7172         },
7173
7174         /**
7175          * Registers a function that will be called when a specified component is added to ComponentMgr
7176          * @param {String} id The component id
7177          * @param {Funtction} fn The callback function
7178          * @param {Object} scope The scope of the callback
7179          */
7180         onAvailable : function(id, fn, scope){
7181             all.on("add", function(index, o){
7182                 if(o.id == id){
7183                     fn.call(scope || o, o);
7184                     all.un("add", fn, scope);
7185                 }
7186             });
7187         }
7188     };
7189 }();/*
7190  * Based on:
7191  * Ext JS Library 1.1.1
7192  * Copyright(c) 2006-2007, Ext JS, LLC.
7193  *
7194  * Originally Released Under LGPL - original licence link has changed is not relivant.
7195  *
7196  * Fork - LGPL
7197  * <script type="text/javascript">
7198  */
7199  
7200 /**
7201  * @class Roo.Component
7202  * @extends Roo.util.Observable
7203  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7204  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7205  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7206  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7207  * All visual components (widgets) that require rendering into a layout should subclass Component.
7208  * @constructor
7209  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7210  * 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
7211  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7212  */
7213 Roo.Component = function(config){
7214     config = config || {};
7215     if(config.tagName || config.dom || typeof config == "string"){ // element object
7216         config = {el: config, id: config.id || config};
7217     }
7218     this.initialConfig = config;
7219
7220     Roo.apply(this, config);
7221     this.addEvents({
7222         /**
7223          * @event disable
7224          * Fires after the component is disabled.
7225              * @param {Roo.Component} this
7226              */
7227         disable : true,
7228         /**
7229          * @event enable
7230          * Fires after the component is enabled.
7231              * @param {Roo.Component} this
7232              */
7233         enable : true,
7234         /**
7235          * @event beforeshow
7236          * Fires before the component is shown.  Return false to stop the show.
7237              * @param {Roo.Component} this
7238              */
7239         beforeshow : true,
7240         /**
7241          * @event show
7242          * Fires after the component is shown.
7243              * @param {Roo.Component} this
7244              */
7245         show : true,
7246         /**
7247          * @event beforehide
7248          * Fires before the component is hidden. Return false to stop the hide.
7249              * @param {Roo.Component} this
7250              */
7251         beforehide : true,
7252         /**
7253          * @event hide
7254          * Fires after the component is hidden.
7255              * @param {Roo.Component} this
7256              */
7257         hide : true,
7258         /**
7259          * @event beforerender
7260          * Fires before the component is rendered. Return false to stop the render.
7261              * @param {Roo.Component} this
7262              */
7263         beforerender : true,
7264         /**
7265          * @event render
7266          * Fires after the component is rendered.
7267              * @param {Roo.Component} this
7268              */
7269         render : true,
7270         /**
7271          * @event beforedestroy
7272          * Fires before the component is destroyed. Return false to stop the destroy.
7273              * @param {Roo.Component} this
7274              */
7275         beforedestroy : true,
7276         /**
7277          * @event destroy
7278          * Fires after the component is destroyed.
7279              * @param {Roo.Component} this
7280              */
7281         destroy : true
7282     });
7283     if(!this.id){
7284         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7285     }
7286     Roo.ComponentMgr.register(this);
7287     Roo.Component.superclass.constructor.call(this);
7288     this.initComponent();
7289     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7290         this.render(this.renderTo);
7291         delete this.renderTo;
7292     }
7293 };
7294
7295 // private
7296 Roo.Component.AUTO_ID = 1000;
7297
7298 Roo.extend(Roo.Component, Roo.util.Observable, {
7299     /**
7300      * @property {Boolean} hidden
7301      * true if this component is hidden. Read-only.
7302      */
7303     hidden : false,
7304     /**
7305      * true if this component is disabled. Read-only.
7306      */
7307     disabled : false,
7308     /**
7309      * true if this component has been rendered. Read-only.
7310      */
7311     rendered : false,
7312     
7313     /** @cfg {String} disableClass
7314      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7315      */
7316     disabledClass : "x-item-disabled",
7317         /** @cfg {Boolean} allowDomMove
7318          * Whether the component can move the Dom node when rendering (defaults to true).
7319          */
7320     allowDomMove : true,
7321     /** @cfg {String} hideMode
7322      * How this component should hidden. Supported values are
7323      * "visibility" (css visibility), "offsets" (negative offset position) and
7324      * "display" (css display) - defaults to "display".
7325      */
7326     hideMode: 'display',
7327
7328     // private
7329     ctype : "Roo.Component",
7330
7331     /** @cfg {String} actionMode 
7332      * which property holds the element that used for  hide() / show() / disable() / enable()
7333      * default is 'el' 
7334      */
7335     actionMode : "el",
7336
7337     // private
7338     getActionEl : function(){
7339         return this[this.actionMode];
7340     },
7341
7342     initComponent : Roo.emptyFn,
7343     /**
7344      * If this is a lazy rendering component, render it to its container element.
7345      * @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.
7346      */
7347     render : function(container, position){
7348         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7349             if(!container && this.el){
7350                 this.el = Roo.get(this.el);
7351                 container = this.el.dom.parentNode;
7352                 this.allowDomMove = false;
7353             }
7354             this.container = Roo.get(container);
7355             this.rendered = true;
7356             if(position !== undefined){
7357                 if(typeof position == 'number'){
7358                     position = this.container.dom.childNodes[position];
7359                 }else{
7360                     position = Roo.getDom(position);
7361                 }
7362             }
7363             this.onRender(this.container, position || null);
7364             if(this.cls){
7365                 this.el.addClass(this.cls);
7366                 delete this.cls;
7367             }
7368             if(this.style){
7369                 this.el.applyStyles(this.style);
7370                 delete this.style;
7371             }
7372             this.fireEvent("render", this);
7373             this.afterRender(this.container);
7374             if(this.hidden){
7375                 this.hide();
7376             }
7377             if(this.disabled){
7378                 this.disable();
7379             }
7380         }
7381         return this;
7382     },
7383
7384     // private
7385     // default function is not really useful
7386     onRender : function(ct, position){
7387         if(this.el){
7388             this.el = Roo.get(this.el);
7389             if(this.allowDomMove !== false){
7390                 ct.dom.insertBefore(this.el.dom, position);
7391             }
7392         }
7393     },
7394
7395     // private
7396     getAutoCreate : function(){
7397         var cfg = typeof this.autoCreate == "object" ?
7398                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7399         if(this.id && !cfg.id){
7400             cfg.id = this.id;
7401         }
7402         return cfg;
7403     },
7404
7405     // private
7406     afterRender : Roo.emptyFn,
7407
7408     /**
7409      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7410      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7411      */
7412     destroy : function(){
7413         if(this.fireEvent("beforedestroy", this) !== false){
7414             this.purgeListeners();
7415             this.beforeDestroy();
7416             if(this.rendered){
7417                 this.el.removeAllListeners();
7418                 this.el.remove();
7419                 if(this.actionMode == "container"){
7420                     this.container.remove();
7421                 }
7422             }
7423             this.onDestroy();
7424             Roo.ComponentMgr.unregister(this);
7425             this.fireEvent("destroy", this);
7426         }
7427     },
7428
7429         // private
7430     beforeDestroy : function(){
7431
7432     },
7433
7434         // private
7435         onDestroy : function(){
7436
7437     },
7438
7439     /**
7440      * Returns the underlying {@link Roo.Element}.
7441      * @return {Roo.Element} The element
7442      */
7443     getEl : function(){
7444         return this.el;
7445     },
7446
7447     /**
7448      * Returns the id of this component.
7449      * @return {String}
7450      */
7451     getId : function(){
7452         return this.id;
7453     },
7454
7455     /**
7456      * Try to focus this component.
7457      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7458      * @return {Roo.Component} this
7459      */
7460     focus : function(selectText){
7461         if(this.rendered){
7462             this.el.focus();
7463             if(selectText === true){
7464                 this.el.dom.select();
7465             }
7466         }
7467         return this;
7468     },
7469
7470     // private
7471     blur : function(){
7472         if(this.rendered){
7473             this.el.blur();
7474         }
7475         return this;
7476     },
7477
7478     /**
7479      * Disable this component.
7480      * @return {Roo.Component} this
7481      */
7482     disable : function(){
7483         if(this.rendered){
7484             this.onDisable();
7485         }
7486         this.disabled = true;
7487         this.fireEvent("disable", this);
7488         return this;
7489     },
7490
7491         // private
7492     onDisable : function(){
7493         this.getActionEl().addClass(this.disabledClass);
7494         this.el.dom.disabled = true;
7495     },
7496
7497     /**
7498      * Enable this component.
7499      * @return {Roo.Component} this
7500      */
7501     enable : function(){
7502         if(this.rendered){
7503             this.onEnable();
7504         }
7505         this.disabled = false;
7506         this.fireEvent("enable", this);
7507         return this;
7508     },
7509
7510         // private
7511     onEnable : function(){
7512         this.getActionEl().removeClass(this.disabledClass);
7513         this.el.dom.disabled = false;
7514     },
7515
7516     /**
7517      * Convenience function for setting disabled/enabled by boolean.
7518      * @param {Boolean} disabled
7519      */
7520     setDisabled : function(disabled){
7521         this[disabled ? "disable" : "enable"]();
7522     },
7523
7524     /**
7525      * Show this component.
7526      * @return {Roo.Component} this
7527      */
7528     show: function(){
7529         if(this.fireEvent("beforeshow", this) !== false){
7530             this.hidden = false;
7531             if(this.rendered){
7532                 this.onShow();
7533             }
7534             this.fireEvent("show", this);
7535         }
7536         return this;
7537     },
7538
7539     // private
7540     onShow : function(){
7541         var ae = this.getActionEl();
7542         if(this.hideMode == 'visibility'){
7543             ae.dom.style.visibility = "visible";
7544         }else if(this.hideMode == 'offsets'){
7545             ae.removeClass('x-hidden');
7546         }else{
7547             ae.dom.style.display = "";
7548         }
7549     },
7550
7551     /**
7552      * Hide this component.
7553      * @return {Roo.Component} this
7554      */
7555     hide: function(){
7556         if(this.fireEvent("beforehide", this) !== false){
7557             this.hidden = true;
7558             if(this.rendered){
7559                 this.onHide();
7560             }
7561             this.fireEvent("hide", this);
7562         }
7563         return this;
7564     },
7565
7566     // private
7567     onHide : function(){
7568         var ae = this.getActionEl();
7569         if(this.hideMode == 'visibility'){
7570             ae.dom.style.visibility = "hidden";
7571         }else if(this.hideMode == 'offsets'){
7572             ae.addClass('x-hidden');
7573         }else{
7574             ae.dom.style.display = "none";
7575         }
7576     },
7577
7578     /**
7579      * Convenience function to hide or show this component by boolean.
7580      * @param {Boolean} visible True to show, false to hide
7581      * @return {Roo.Component} this
7582      */
7583     setVisible: function(visible){
7584         if(visible) {
7585             this.show();
7586         }else{
7587             this.hide();
7588         }
7589         return this;
7590     },
7591
7592     /**
7593      * Returns true if this component is visible.
7594      */
7595     isVisible : function(){
7596         return this.getActionEl().isVisible();
7597     },
7598
7599     cloneConfig : function(overrides){
7600         overrides = overrides || {};
7601         var id = overrides.id || Roo.id();
7602         var cfg = Roo.applyIf(overrides, this.initialConfig);
7603         cfg.id = id; // prevent dup id
7604         return new this.constructor(cfg);
7605     }
7606 });/*
7607  * Based on:
7608  * Ext JS Library 1.1.1
7609  * Copyright(c) 2006-2007, Ext JS, LLC.
7610  *
7611  * Originally Released Under LGPL - original licence link has changed is not relivant.
7612  *
7613  * Fork - LGPL
7614  * <script type="text/javascript">
7615  */
7616  (function(){ 
7617 /**
7618  * @class Roo.Layer
7619  * @extends Roo.Element
7620  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7621  * automatic maintaining of shadow/shim positions.
7622  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7623  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7624  * you can pass a string with a CSS class name. False turns off the shadow.
7625  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7626  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7627  * @cfg {String} cls CSS class to add to the element
7628  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7629  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7630  * @constructor
7631  * @param {Object} config An object with config options.
7632  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7633  */
7634
7635 Roo.Layer = function(config, existingEl){
7636     config = config || {};
7637     var dh = Roo.DomHelper;
7638     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7639     if(existingEl){
7640         this.dom = Roo.getDom(existingEl);
7641     }
7642     if(!this.dom){
7643         var o = config.dh || {tag: "div", cls: "x-layer"};
7644         this.dom = dh.append(pel, o);
7645     }
7646     if(config.cls){
7647         this.addClass(config.cls);
7648     }
7649     this.constrain = config.constrain !== false;
7650     this.visibilityMode = Roo.Element.VISIBILITY;
7651     if(config.id){
7652         this.id = this.dom.id = config.id;
7653     }else{
7654         this.id = Roo.id(this.dom);
7655     }
7656     this.zindex = config.zindex || this.getZIndex();
7657     this.position("absolute", this.zindex);
7658     if(config.shadow){
7659         this.shadowOffset = config.shadowOffset || 4;
7660         this.shadow = new Roo.Shadow({
7661             offset : this.shadowOffset,
7662             mode : config.shadow
7663         });
7664     }else{
7665         this.shadowOffset = 0;
7666     }
7667     this.useShim = config.shim !== false && Roo.useShims;
7668     this.useDisplay = config.useDisplay;
7669     this.hide();
7670 };
7671
7672 var supr = Roo.Element.prototype;
7673
7674 // shims are shared among layer to keep from having 100 iframes
7675 var shims = [];
7676
7677 Roo.extend(Roo.Layer, Roo.Element, {
7678
7679     getZIndex : function(){
7680         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7681     },
7682
7683     getShim : function(){
7684         if(!this.useShim){
7685             return null;
7686         }
7687         if(this.shim){
7688             return this.shim;
7689         }
7690         var shim = shims.shift();
7691         if(!shim){
7692             shim = this.createShim();
7693             shim.enableDisplayMode('block');
7694             shim.dom.style.display = 'none';
7695             shim.dom.style.visibility = 'visible';
7696         }
7697         var pn = this.dom.parentNode;
7698         if(shim.dom.parentNode != pn){
7699             pn.insertBefore(shim.dom, this.dom);
7700         }
7701         shim.setStyle('z-index', this.getZIndex()-2);
7702         this.shim = shim;
7703         return shim;
7704     },
7705
7706     hideShim : function(){
7707         if(this.shim){
7708             this.shim.setDisplayed(false);
7709             shims.push(this.shim);
7710             delete this.shim;
7711         }
7712     },
7713
7714     disableShadow : function(){
7715         if(this.shadow){
7716             this.shadowDisabled = true;
7717             this.shadow.hide();
7718             this.lastShadowOffset = this.shadowOffset;
7719             this.shadowOffset = 0;
7720         }
7721     },
7722
7723     enableShadow : function(show){
7724         if(this.shadow){
7725             this.shadowDisabled = false;
7726             this.shadowOffset = this.lastShadowOffset;
7727             delete this.lastShadowOffset;
7728             if(show){
7729                 this.sync(true);
7730             }
7731         }
7732     },
7733
7734     // private
7735     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7736     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7737     sync : function(doShow){
7738         var sw = this.shadow;
7739         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7740             var sh = this.getShim();
7741
7742             var w = this.getWidth(),
7743                 h = this.getHeight();
7744
7745             var l = this.getLeft(true),
7746                 t = this.getTop(true);
7747
7748             if(sw && !this.shadowDisabled){
7749                 if(doShow && !sw.isVisible()){
7750                     sw.show(this);
7751                 }else{
7752                     sw.realign(l, t, w, h);
7753                 }
7754                 if(sh){
7755                     if(doShow){
7756                        sh.show();
7757                     }
7758                     // fit the shim behind the shadow, so it is shimmed too
7759                     var a = sw.adjusts, s = sh.dom.style;
7760                     s.left = (Math.min(l, l+a.l))+"px";
7761                     s.top = (Math.min(t, t+a.t))+"px";
7762                     s.width = (w+a.w)+"px";
7763                     s.height = (h+a.h)+"px";
7764                 }
7765             }else if(sh){
7766                 if(doShow){
7767                    sh.show();
7768                 }
7769                 sh.setSize(w, h);
7770                 sh.setLeftTop(l, t);
7771             }
7772             
7773         }
7774     },
7775
7776     // private
7777     destroy : function(){
7778         this.hideShim();
7779         if(this.shadow){
7780             this.shadow.hide();
7781         }
7782         this.removeAllListeners();
7783         var pn = this.dom.parentNode;
7784         if(pn){
7785             pn.removeChild(this.dom);
7786         }
7787         Roo.Element.uncache(this.id);
7788     },
7789
7790     remove : function(){
7791         this.destroy();
7792     },
7793
7794     // private
7795     beginUpdate : function(){
7796         this.updating = true;
7797     },
7798
7799     // private
7800     endUpdate : function(){
7801         this.updating = false;
7802         this.sync(true);
7803     },
7804
7805     // private
7806     hideUnders : function(negOffset){
7807         if(this.shadow){
7808             this.shadow.hide();
7809         }
7810         this.hideShim();
7811     },
7812
7813     // private
7814     constrainXY : function(){
7815         if(this.constrain){
7816             var vw = Roo.lib.Dom.getViewWidth(),
7817                 vh = Roo.lib.Dom.getViewHeight();
7818             var s = Roo.get(document).getScroll();
7819
7820             var xy = this.getXY();
7821             var x = xy[0], y = xy[1];   
7822             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7823             // only move it if it needs it
7824             var moved = false;
7825             // first validate right/bottom
7826             if((x + w) > vw+s.left){
7827                 x = vw - w - this.shadowOffset;
7828                 moved = true;
7829             }
7830             if((y + h) > vh+s.top){
7831                 y = vh - h - this.shadowOffset;
7832                 moved = true;
7833             }
7834             // then make sure top/left isn't negative
7835             if(x < s.left){
7836                 x = s.left;
7837                 moved = true;
7838             }
7839             if(y < s.top){
7840                 y = s.top;
7841                 moved = true;
7842             }
7843             if(moved){
7844                 if(this.avoidY){
7845                     var ay = this.avoidY;
7846                     if(y <= ay && (y+h) >= ay){
7847                         y = ay-h-5;   
7848                     }
7849                 }
7850                 xy = [x, y];
7851                 this.storeXY(xy);
7852                 supr.setXY.call(this, xy);
7853                 this.sync();
7854             }
7855         }
7856     },
7857
7858     isVisible : function(){
7859         return this.visible;    
7860     },
7861
7862     // private
7863     showAction : function(){
7864         this.visible = true; // track visibility to prevent getStyle calls
7865         if(this.useDisplay === true){
7866             this.setDisplayed("");
7867         }else if(this.lastXY){
7868             supr.setXY.call(this, this.lastXY);
7869         }else if(this.lastLT){
7870             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7871         }
7872     },
7873
7874     // private
7875     hideAction : function(){
7876         this.visible = false;
7877         if(this.useDisplay === true){
7878             this.setDisplayed(false);
7879         }else{
7880             this.setLeftTop(-10000,-10000);
7881         }
7882     },
7883
7884     // overridden Element method
7885     setVisible : function(v, a, d, c, e){
7886         if(v){
7887             this.showAction();
7888         }
7889         if(a && v){
7890             var cb = function(){
7891                 this.sync(true);
7892                 if(c){
7893                     c();
7894                 }
7895             }.createDelegate(this);
7896             supr.setVisible.call(this, true, true, d, cb, e);
7897         }else{
7898             if(!v){
7899                 this.hideUnders(true);
7900             }
7901             var cb = c;
7902             if(a){
7903                 cb = function(){
7904                     this.hideAction();
7905                     if(c){
7906                         c();
7907                     }
7908                 }.createDelegate(this);
7909             }
7910             supr.setVisible.call(this, v, a, d, cb, e);
7911             if(v){
7912                 this.sync(true);
7913             }else if(!a){
7914                 this.hideAction();
7915             }
7916         }
7917     },
7918
7919     storeXY : function(xy){
7920         delete this.lastLT;
7921         this.lastXY = xy;
7922     },
7923
7924     storeLeftTop : function(left, top){
7925         delete this.lastXY;
7926         this.lastLT = [left, top];
7927     },
7928
7929     // private
7930     beforeFx : function(){
7931         this.beforeAction();
7932         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7933     },
7934
7935     // private
7936     afterFx : function(){
7937         Roo.Layer.superclass.afterFx.apply(this, arguments);
7938         this.sync(this.isVisible());
7939     },
7940
7941     // private
7942     beforeAction : function(){
7943         if(!this.updating && this.shadow){
7944             this.shadow.hide();
7945         }
7946     },
7947
7948     // overridden Element method
7949     setLeft : function(left){
7950         this.storeLeftTop(left, this.getTop(true));
7951         supr.setLeft.apply(this, arguments);
7952         this.sync();
7953     },
7954
7955     setTop : function(top){
7956         this.storeLeftTop(this.getLeft(true), top);
7957         supr.setTop.apply(this, arguments);
7958         this.sync();
7959     },
7960
7961     setLeftTop : function(left, top){
7962         this.storeLeftTop(left, top);
7963         supr.setLeftTop.apply(this, arguments);
7964         this.sync();
7965     },
7966
7967     setXY : function(xy, a, d, c, e){
7968         this.fixDisplay();
7969         this.beforeAction();
7970         this.storeXY(xy);
7971         var cb = this.createCB(c);
7972         supr.setXY.call(this, xy, a, d, cb, e);
7973         if(!a){
7974             cb();
7975         }
7976     },
7977
7978     // private
7979     createCB : function(c){
7980         var el = this;
7981         return function(){
7982             el.constrainXY();
7983             el.sync(true);
7984             if(c){
7985                 c();
7986             }
7987         };
7988     },
7989
7990     // overridden Element method
7991     setX : function(x, a, d, c, e){
7992         this.setXY([x, this.getY()], a, d, c, e);
7993     },
7994
7995     // overridden Element method
7996     setY : function(y, a, d, c, e){
7997         this.setXY([this.getX(), y], a, d, c, e);
7998     },
7999
8000     // overridden Element method
8001     setSize : function(w, h, a, d, c, e){
8002         this.beforeAction();
8003         var cb = this.createCB(c);
8004         supr.setSize.call(this, w, h, a, d, cb, e);
8005         if(!a){
8006             cb();
8007         }
8008     },
8009
8010     // overridden Element method
8011     setWidth : function(w, a, d, c, e){
8012         this.beforeAction();
8013         var cb = this.createCB(c);
8014         supr.setWidth.call(this, w, a, d, cb, e);
8015         if(!a){
8016             cb();
8017         }
8018     },
8019
8020     // overridden Element method
8021     setHeight : function(h, a, d, c, e){
8022         this.beforeAction();
8023         var cb = this.createCB(c);
8024         supr.setHeight.call(this, h, a, d, cb, e);
8025         if(!a){
8026             cb();
8027         }
8028     },
8029
8030     // overridden Element method
8031     setBounds : function(x, y, w, h, a, d, c, e){
8032         this.beforeAction();
8033         var cb = this.createCB(c);
8034         if(!a){
8035             this.storeXY([x, y]);
8036             supr.setXY.call(this, [x, y]);
8037             supr.setSize.call(this, w, h, a, d, cb, e);
8038             cb();
8039         }else{
8040             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8041         }
8042         return this;
8043     },
8044     
8045     /**
8046      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8047      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8048      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8049      * @param {Number} zindex The new z-index to set
8050      * @return {this} The Layer
8051      */
8052     setZIndex : function(zindex){
8053         this.zindex = zindex;
8054         this.setStyle("z-index", zindex + 2);
8055         if(this.shadow){
8056             this.shadow.setZIndex(zindex + 1);
8057         }
8058         if(this.shim){
8059             this.shim.setStyle("z-index", zindex);
8060         }
8061     }
8062 });
8063 })();/*
8064  * Based on:
8065  * Ext JS Library 1.1.1
8066  * Copyright(c) 2006-2007, Ext JS, LLC.
8067  *
8068  * Originally Released Under LGPL - original licence link has changed is not relivant.
8069  *
8070  * Fork - LGPL
8071  * <script type="text/javascript">
8072  */
8073
8074
8075 /**
8076  * @class Roo.Shadow
8077  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8078  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8079  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8080  * @constructor
8081  * Create a new Shadow
8082  * @param {Object} config The config object
8083  */
8084 Roo.Shadow = function(config){
8085     Roo.apply(this, config);
8086     if(typeof this.mode != "string"){
8087         this.mode = this.defaultMode;
8088     }
8089     var o = this.offset, a = {h: 0};
8090     var rad = Math.floor(this.offset/2);
8091     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8092         case "drop":
8093             a.w = 0;
8094             a.l = a.t = o;
8095             a.t -= 1;
8096             if(Roo.isIE){
8097                 a.l -= this.offset + rad;
8098                 a.t -= this.offset + rad;
8099                 a.w -= rad;
8100                 a.h -= rad;
8101                 a.t += 1;
8102             }
8103         break;
8104         case "sides":
8105             a.w = (o*2);
8106             a.l = -o;
8107             a.t = o-1;
8108             if(Roo.isIE){
8109                 a.l -= (this.offset - rad);
8110                 a.t -= this.offset + rad;
8111                 a.l += 1;
8112                 a.w -= (this.offset - rad)*2;
8113                 a.w -= rad + 1;
8114                 a.h -= 1;
8115             }
8116         break;
8117         case "frame":
8118             a.w = a.h = (o*2);
8119             a.l = a.t = -o;
8120             a.t += 1;
8121             a.h -= 2;
8122             if(Roo.isIE){
8123                 a.l -= (this.offset - rad);
8124                 a.t -= (this.offset - rad);
8125                 a.l += 1;
8126                 a.w -= (this.offset + rad + 1);
8127                 a.h -= (this.offset + rad);
8128                 a.h += 1;
8129             }
8130         break;
8131     };
8132
8133     this.adjusts = a;
8134 };
8135
8136 Roo.Shadow.prototype = {
8137     /**
8138      * @cfg {String} mode
8139      * The shadow display mode.  Supports the following options:<br />
8140      * sides: Shadow displays on both sides and bottom only<br />
8141      * frame: Shadow displays equally on all four sides<br />
8142      * drop: Traditional bottom-right drop shadow (default)
8143      */
8144     /**
8145      * @cfg {String} offset
8146      * The number of pixels to offset the shadow from the element (defaults to 4)
8147      */
8148     offset: 4,
8149
8150     // private
8151     defaultMode: "drop",
8152
8153     /**
8154      * Displays the shadow under the target element
8155      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8156      */
8157     show : function(target){
8158         target = Roo.get(target);
8159         if(!this.el){
8160             this.el = Roo.Shadow.Pool.pull();
8161             if(this.el.dom.nextSibling != target.dom){
8162                 this.el.insertBefore(target);
8163             }
8164         }
8165         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8166         if(Roo.isIE){
8167             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8168         }
8169         this.realign(
8170             target.getLeft(true),
8171             target.getTop(true),
8172             target.getWidth(),
8173             target.getHeight()
8174         );
8175         this.el.dom.style.display = "block";
8176     },
8177
8178     /**
8179      * Returns true if the shadow is visible, else false
8180      */
8181     isVisible : function(){
8182         return this.el ? true : false;  
8183     },
8184
8185     /**
8186      * Direct alignment when values are already available. Show must be called at least once before
8187      * calling this method to ensure it is initialized.
8188      * @param {Number} left The target element left position
8189      * @param {Number} top The target element top position
8190      * @param {Number} width The target element width
8191      * @param {Number} height The target element height
8192      */
8193     realign : function(l, t, w, h){
8194         if(!this.el){
8195             return;
8196         }
8197         var a = this.adjusts, d = this.el.dom, s = d.style;
8198         var iea = 0;
8199         s.left = (l+a.l)+"px";
8200         s.top = (t+a.t)+"px";
8201         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8202  
8203         if(s.width != sws || s.height != shs){
8204             s.width = sws;
8205             s.height = shs;
8206             if(!Roo.isIE){
8207                 var cn = d.childNodes;
8208                 var sww = Math.max(0, (sw-12))+"px";
8209                 cn[0].childNodes[1].style.width = sww;
8210                 cn[1].childNodes[1].style.width = sww;
8211                 cn[2].childNodes[1].style.width = sww;
8212                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8213             }
8214         }
8215     },
8216
8217     /**
8218      * Hides this shadow
8219      */
8220     hide : function(){
8221         if(this.el){
8222             this.el.dom.style.display = "none";
8223             Roo.Shadow.Pool.push(this.el);
8224             delete this.el;
8225         }
8226     },
8227
8228     /**
8229      * Adjust the z-index of this shadow
8230      * @param {Number} zindex The new z-index
8231      */
8232     setZIndex : function(z){
8233         this.zIndex = z;
8234         if(this.el){
8235             this.el.setStyle("z-index", z);
8236         }
8237     }
8238 };
8239
8240 // Private utility class that manages the internal Shadow cache
8241 Roo.Shadow.Pool = function(){
8242     var p = [];
8243     var markup = Roo.isIE ?
8244                  '<div class="x-ie-shadow"></div>' :
8245                  '<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>';
8246     return {
8247         pull : function(){
8248             var sh = p.shift();
8249             if(!sh){
8250                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8251                 sh.autoBoxAdjust = false;
8252             }
8253             return sh;
8254         },
8255
8256         push : function(sh){
8257             p.push(sh);
8258         }
8259     };
8260 }();/*
8261  * Based on:
8262  * Ext JS Library 1.1.1
8263  * Copyright(c) 2006-2007, Ext JS, LLC.
8264  *
8265  * Originally Released Under LGPL - original licence link has changed is not relivant.
8266  *
8267  * Fork - LGPL
8268  * <script type="text/javascript">
8269  */
8270
8271 /**
8272  * @class Roo.BoxComponent
8273  * @extends Roo.Component
8274  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8275  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8276  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8277  * layout containers.
8278  * @constructor
8279  * @param {Roo.Element/String/Object} config The configuration options.
8280  */
8281 Roo.BoxComponent = function(config){
8282     Roo.Component.call(this, config);
8283     this.addEvents({
8284         /**
8285          * @event resize
8286          * Fires after the component is resized.
8287              * @param {Roo.Component} this
8288              * @param {Number} adjWidth The box-adjusted width that was set
8289              * @param {Number} adjHeight The box-adjusted height that was set
8290              * @param {Number} rawWidth The width that was originally specified
8291              * @param {Number} rawHeight The height that was originally specified
8292              */
8293         resize : true,
8294         /**
8295          * @event move
8296          * Fires after the component is moved.
8297              * @param {Roo.Component} this
8298              * @param {Number} x The new x position
8299              * @param {Number} y The new y position
8300              */
8301         move : true
8302     });
8303 };
8304
8305 Roo.extend(Roo.BoxComponent, Roo.Component, {
8306     // private, set in afterRender to signify that the component has been rendered
8307     boxReady : false,
8308     // private, used to defer height settings to subclasses
8309     deferHeight: false,
8310     /** @cfg {Number} width
8311      * width (optional) size of component
8312      */
8313      /** @cfg {Number} height
8314      * height (optional) size of component
8315      */
8316      
8317     /**
8318      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8319      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8320      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8321      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8322      * @return {Roo.BoxComponent} this
8323      */
8324     setSize : function(w, h){
8325         // support for standard size objects
8326         if(typeof w == 'object'){
8327             h = w.height;
8328             w = w.width;
8329         }
8330         // not rendered
8331         if(!this.boxReady){
8332             this.width = w;
8333             this.height = h;
8334             return this;
8335         }
8336
8337         // prevent recalcs when not needed
8338         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8339             return this;
8340         }
8341         this.lastSize = {width: w, height: h};
8342
8343         var adj = this.adjustSize(w, h);
8344         var aw = adj.width, ah = adj.height;
8345         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8346             var rz = this.getResizeEl();
8347             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8348                 rz.setSize(aw, ah);
8349             }else if(!this.deferHeight && ah !== undefined){
8350                 rz.setHeight(ah);
8351             }else if(aw !== undefined){
8352                 rz.setWidth(aw);
8353             }
8354             this.onResize(aw, ah, w, h);
8355             this.fireEvent('resize', this, aw, ah, w, h);
8356         }
8357         return this;
8358     },
8359
8360     /**
8361      * Gets the current size of the component's underlying element.
8362      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8363      */
8364     getSize : function(){
8365         return this.el.getSize();
8366     },
8367
8368     /**
8369      * Gets the current XY position of the component's underlying element.
8370      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8371      * @return {Array} The XY position of the element (e.g., [100, 200])
8372      */
8373     getPosition : function(local){
8374         if(local === true){
8375             return [this.el.getLeft(true), this.el.getTop(true)];
8376         }
8377         return this.xy || this.el.getXY();
8378     },
8379
8380     /**
8381      * Gets the current box measurements of the component's underlying element.
8382      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8383      * @returns {Object} box An object in the format {x, y, width, height}
8384      */
8385     getBox : function(local){
8386         var s = this.el.getSize();
8387         if(local){
8388             s.x = this.el.getLeft(true);
8389             s.y = this.el.getTop(true);
8390         }else{
8391             var xy = this.xy || this.el.getXY();
8392             s.x = xy[0];
8393             s.y = xy[1];
8394         }
8395         return s;
8396     },
8397
8398     /**
8399      * Sets the current box measurements of the component's underlying element.
8400      * @param {Object} box An object in the format {x, y, width, height}
8401      * @returns {Roo.BoxComponent} this
8402      */
8403     updateBox : function(box){
8404         this.setSize(box.width, box.height);
8405         this.setPagePosition(box.x, box.y);
8406         return this;
8407     },
8408
8409     // protected
8410     getResizeEl : function(){
8411         return this.resizeEl || this.el;
8412     },
8413
8414     // protected
8415     getPositionEl : function(){
8416         return this.positionEl || this.el;
8417     },
8418
8419     /**
8420      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8421      * This method fires the move event.
8422      * @param {Number} left The new left
8423      * @param {Number} top The new top
8424      * @returns {Roo.BoxComponent} this
8425      */
8426     setPosition : function(x, y){
8427         this.x = x;
8428         this.y = y;
8429         if(!this.boxReady){
8430             return this;
8431         }
8432         var adj = this.adjustPosition(x, y);
8433         var ax = adj.x, ay = adj.y;
8434
8435         var el = this.getPositionEl();
8436         if(ax !== undefined || ay !== undefined){
8437             if(ax !== undefined && ay !== undefined){
8438                 el.setLeftTop(ax, ay);
8439             }else if(ax !== undefined){
8440                 el.setLeft(ax);
8441             }else if(ay !== undefined){
8442                 el.setTop(ay);
8443             }
8444             this.onPosition(ax, ay);
8445             this.fireEvent('move', this, ax, ay);
8446         }
8447         return this;
8448     },
8449
8450     /**
8451      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8452      * This method fires the move event.
8453      * @param {Number} x The new x position
8454      * @param {Number} y The new y position
8455      * @returns {Roo.BoxComponent} this
8456      */
8457     setPagePosition : function(x, y){
8458         this.pageX = x;
8459         this.pageY = y;
8460         if(!this.boxReady){
8461             return;
8462         }
8463         if(x === undefined || y === undefined){ // cannot translate undefined points
8464             return;
8465         }
8466         var p = this.el.translatePoints(x, y);
8467         this.setPosition(p.left, p.top);
8468         return this;
8469     },
8470
8471     // private
8472     onRender : function(ct, position){
8473         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8474         if(this.resizeEl){
8475             this.resizeEl = Roo.get(this.resizeEl);
8476         }
8477         if(this.positionEl){
8478             this.positionEl = Roo.get(this.positionEl);
8479         }
8480     },
8481
8482     // private
8483     afterRender : function(){
8484         Roo.BoxComponent.superclass.afterRender.call(this);
8485         this.boxReady = true;
8486         this.setSize(this.width, this.height);
8487         if(this.x || this.y){
8488             this.setPosition(this.x, this.y);
8489         }
8490         if(this.pageX || this.pageY){
8491             this.setPagePosition(this.pageX, this.pageY);
8492         }
8493     },
8494
8495     /**
8496      * Force the component's size to recalculate based on the underlying element's current height and width.
8497      * @returns {Roo.BoxComponent} this
8498      */
8499     syncSize : function(){
8500         delete this.lastSize;
8501         this.setSize(this.el.getWidth(), this.el.getHeight());
8502         return this;
8503     },
8504
8505     /**
8506      * Called after the component is resized, this method is empty by default but can be implemented by any
8507      * subclass that needs to perform custom logic after a resize occurs.
8508      * @param {Number} adjWidth The box-adjusted width that was set
8509      * @param {Number} adjHeight The box-adjusted height that was set
8510      * @param {Number} rawWidth The width that was originally specified
8511      * @param {Number} rawHeight The height that was originally specified
8512      */
8513     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8514
8515     },
8516
8517     /**
8518      * Called after the component is moved, this method is empty by default but can be implemented by any
8519      * subclass that needs to perform custom logic after a move occurs.
8520      * @param {Number} x The new x position
8521      * @param {Number} y The new y position
8522      */
8523     onPosition : function(x, y){
8524
8525     },
8526
8527     // private
8528     adjustSize : function(w, h){
8529         if(this.autoWidth){
8530             w = 'auto';
8531         }
8532         if(this.autoHeight){
8533             h = 'auto';
8534         }
8535         return {width : w, height: h};
8536     },
8537
8538     // private
8539     adjustPosition : function(x, y){
8540         return {x : x, y: y};
8541     }
8542 });/*
8543  * Based on:
8544  * Ext JS Library 1.1.1
8545  * Copyright(c) 2006-2007, Ext JS, LLC.
8546  *
8547  * Originally Released Under LGPL - original licence link has changed is not relivant.
8548  *
8549  * Fork - LGPL
8550  * <script type="text/javascript">
8551  */
8552
8553
8554 /**
8555  * @class Roo.SplitBar
8556  * @extends Roo.util.Observable
8557  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8558  * <br><br>
8559  * Usage:
8560  * <pre><code>
8561 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8562                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8563 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8564 split.minSize = 100;
8565 split.maxSize = 600;
8566 split.animate = true;
8567 split.on('moved', splitterMoved);
8568 </code></pre>
8569  * @constructor
8570  * Create a new SplitBar
8571  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8572  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8573  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8574  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8575                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8576                         position of the SplitBar).
8577  */
8578 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8579     
8580     /** @private */
8581     this.el = Roo.get(dragElement, true);
8582     this.el.dom.unselectable = "on";
8583     /** @private */
8584     this.resizingEl = Roo.get(resizingElement, true);
8585
8586     /**
8587      * @private
8588      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8589      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8590      * @type Number
8591      */
8592     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8593     
8594     /**
8595      * The minimum size of the resizing element. (Defaults to 0)
8596      * @type Number
8597      */
8598     this.minSize = 0;
8599     
8600     /**
8601      * The maximum size of the resizing element. (Defaults to 2000)
8602      * @type Number
8603      */
8604     this.maxSize = 2000;
8605     
8606     /**
8607      * Whether to animate the transition to the new size
8608      * @type Boolean
8609      */
8610     this.animate = false;
8611     
8612     /**
8613      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8614      * @type Boolean
8615      */
8616     this.useShim = false;
8617     
8618     /** @private */
8619     this.shim = null;
8620     
8621     if(!existingProxy){
8622         /** @private */
8623         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8624     }else{
8625         this.proxy = Roo.get(existingProxy).dom;
8626     }
8627     /** @private */
8628     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8629     
8630     /** @private */
8631     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8632     
8633     /** @private */
8634     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8635     
8636     /** @private */
8637     this.dragSpecs = {};
8638     
8639     /**
8640      * @private The adapter to use to positon and resize elements
8641      */
8642     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8643     this.adapter.init(this);
8644     
8645     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8646         /** @private */
8647         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8648         this.el.addClass("x-splitbar-h");
8649     }else{
8650         /** @private */
8651         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8652         this.el.addClass("x-splitbar-v");
8653     }
8654     
8655     this.addEvents({
8656         /**
8657          * @event resize
8658          * Fires when the splitter is moved (alias for {@link #event-moved})
8659          * @param {Roo.SplitBar} this
8660          * @param {Number} newSize the new width or height
8661          */
8662         "resize" : true,
8663         /**
8664          * @event moved
8665          * Fires when the splitter is moved
8666          * @param {Roo.SplitBar} this
8667          * @param {Number} newSize the new width or height
8668          */
8669         "moved" : true,
8670         /**
8671          * @event beforeresize
8672          * Fires before the splitter is dragged
8673          * @param {Roo.SplitBar} this
8674          */
8675         "beforeresize" : true,
8676
8677         "beforeapply" : true
8678     });
8679
8680     Roo.util.Observable.call(this);
8681 };
8682
8683 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8684     onStartProxyDrag : function(x, y){
8685         this.fireEvent("beforeresize", this);
8686         if(!this.overlay){
8687             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8688             o.unselectable();
8689             o.enableDisplayMode("block");
8690             // all splitbars share the same overlay
8691             Roo.SplitBar.prototype.overlay = o;
8692         }
8693         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8694         this.overlay.show();
8695         Roo.get(this.proxy).setDisplayed("block");
8696         var size = this.adapter.getElementSize(this);
8697         this.activeMinSize = this.getMinimumSize();;
8698         this.activeMaxSize = this.getMaximumSize();;
8699         var c1 = size - this.activeMinSize;
8700         var c2 = Math.max(this.activeMaxSize - size, 0);
8701         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8702             this.dd.resetConstraints();
8703             this.dd.setXConstraint(
8704                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8705                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8706             );
8707             this.dd.setYConstraint(0, 0);
8708         }else{
8709             this.dd.resetConstraints();
8710             this.dd.setXConstraint(0, 0);
8711             this.dd.setYConstraint(
8712                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8713                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8714             );
8715          }
8716         this.dragSpecs.startSize = size;
8717         this.dragSpecs.startPoint = [x, y];
8718         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8719     },
8720     
8721     /** 
8722      * @private Called after the drag operation by the DDProxy
8723      */
8724     onEndProxyDrag : function(e){
8725         Roo.get(this.proxy).setDisplayed(false);
8726         var endPoint = Roo.lib.Event.getXY(e);
8727         if(this.overlay){
8728             this.overlay.hide();
8729         }
8730         var newSize;
8731         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8732             newSize = this.dragSpecs.startSize + 
8733                 (this.placement == Roo.SplitBar.LEFT ?
8734                     endPoint[0] - this.dragSpecs.startPoint[0] :
8735                     this.dragSpecs.startPoint[0] - endPoint[0]
8736                 );
8737         }else{
8738             newSize = this.dragSpecs.startSize + 
8739                 (this.placement == Roo.SplitBar.TOP ?
8740                     endPoint[1] - this.dragSpecs.startPoint[1] :
8741                     this.dragSpecs.startPoint[1] - endPoint[1]
8742                 );
8743         }
8744         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8745         if(newSize != this.dragSpecs.startSize){
8746             if(this.fireEvent('beforeapply', this, newSize) !== false){
8747                 this.adapter.setElementSize(this, newSize);
8748                 this.fireEvent("moved", this, newSize);
8749                 this.fireEvent("resize", this, newSize);
8750             }
8751         }
8752     },
8753     
8754     /**
8755      * Get the adapter this SplitBar uses
8756      * @return The adapter object
8757      */
8758     getAdapter : function(){
8759         return this.adapter;
8760     },
8761     
8762     /**
8763      * Set the adapter this SplitBar uses
8764      * @param {Object} adapter A SplitBar adapter object
8765      */
8766     setAdapter : function(adapter){
8767         this.adapter = adapter;
8768         this.adapter.init(this);
8769     },
8770     
8771     /**
8772      * Gets the minimum size for the resizing element
8773      * @return {Number} The minimum size
8774      */
8775     getMinimumSize : function(){
8776         return this.minSize;
8777     },
8778     
8779     /**
8780      * Sets the minimum size for the resizing element
8781      * @param {Number} minSize The minimum size
8782      */
8783     setMinimumSize : function(minSize){
8784         this.minSize = minSize;
8785     },
8786     
8787     /**
8788      * Gets the maximum size for the resizing element
8789      * @return {Number} The maximum size
8790      */
8791     getMaximumSize : function(){
8792         return this.maxSize;
8793     },
8794     
8795     /**
8796      * Sets the maximum size for the resizing element
8797      * @param {Number} maxSize The maximum size
8798      */
8799     setMaximumSize : function(maxSize){
8800         this.maxSize = maxSize;
8801     },
8802     
8803     /**
8804      * Sets the initialize size for the resizing element
8805      * @param {Number} size The initial size
8806      */
8807     setCurrentSize : function(size){
8808         var oldAnimate = this.animate;
8809         this.animate = false;
8810         this.adapter.setElementSize(this, size);
8811         this.animate = oldAnimate;
8812     },
8813     
8814     /**
8815      * Destroy this splitbar. 
8816      * @param {Boolean} removeEl True to remove the element
8817      */
8818     destroy : function(removeEl){
8819         if(this.shim){
8820             this.shim.remove();
8821         }
8822         this.dd.unreg();
8823         this.proxy.parentNode.removeChild(this.proxy);
8824         if(removeEl){
8825             this.el.remove();
8826         }
8827     }
8828 });
8829
8830 /**
8831  * @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.
8832  */
8833 Roo.SplitBar.createProxy = function(dir){
8834     var proxy = new Roo.Element(document.createElement("div"));
8835     proxy.unselectable();
8836     var cls = 'x-splitbar-proxy';
8837     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8838     document.body.appendChild(proxy.dom);
8839     return proxy.dom;
8840 };
8841
8842 /** 
8843  * @class Roo.SplitBar.BasicLayoutAdapter
8844  * Default Adapter. It assumes the splitter and resizing element are not positioned
8845  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8846  */
8847 Roo.SplitBar.BasicLayoutAdapter = function(){
8848 };
8849
8850 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8851     // do nothing for now
8852     init : function(s){
8853     
8854     },
8855     /**
8856      * Called before drag operations to get the current size of the resizing element. 
8857      * @param {Roo.SplitBar} s The SplitBar using this adapter
8858      */
8859      getElementSize : function(s){
8860         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8861             return s.resizingEl.getWidth();
8862         }else{
8863             return s.resizingEl.getHeight();
8864         }
8865     },
8866     
8867     /**
8868      * Called after drag operations to set the size of the resizing element.
8869      * @param {Roo.SplitBar} s The SplitBar using this adapter
8870      * @param {Number} newSize The new size to set
8871      * @param {Function} onComplete A function to be invoked when resizing is complete
8872      */
8873     setElementSize : function(s, newSize, onComplete){
8874         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8875             if(!s.animate){
8876                 s.resizingEl.setWidth(newSize);
8877                 if(onComplete){
8878                     onComplete(s, newSize);
8879                 }
8880             }else{
8881                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8882             }
8883         }else{
8884             
8885             if(!s.animate){
8886                 s.resizingEl.setHeight(newSize);
8887                 if(onComplete){
8888                     onComplete(s, newSize);
8889                 }
8890             }else{
8891                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8892             }
8893         }
8894     }
8895 };
8896
8897 /** 
8898  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8899  * @extends Roo.SplitBar.BasicLayoutAdapter
8900  * Adapter that  moves the splitter element to align with the resized sizing element. 
8901  * Used with an absolute positioned SplitBar.
8902  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8903  * document.body, make sure you assign an id to the body element.
8904  */
8905 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8906     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8907     this.container = Roo.get(container);
8908 };
8909
8910 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8911     init : function(s){
8912         this.basic.init(s);
8913     },
8914     
8915     getElementSize : function(s){
8916         return this.basic.getElementSize(s);
8917     },
8918     
8919     setElementSize : function(s, newSize, onComplete){
8920         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8921     },
8922     
8923     moveSplitter : function(s){
8924         var yes = Roo.SplitBar;
8925         switch(s.placement){
8926             case yes.LEFT:
8927                 s.el.setX(s.resizingEl.getRight());
8928                 break;
8929             case yes.RIGHT:
8930                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8931                 break;
8932             case yes.TOP:
8933                 s.el.setY(s.resizingEl.getBottom());
8934                 break;
8935             case yes.BOTTOM:
8936                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8937                 break;
8938         }
8939     }
8940 };
8941
8942 /**
8943  * Orientation constant - Create a vertical SplitBar
8944  * @static
8945  * @type Number
8946  */
8947 Roo.SplitBar.VERTICAL = 1;
8948
8949 /**
8950  * Orientation constant - Create a horizontal SplitBar
8951  * @static
8952  * @type Number
8953  */
8954 Roo.SplitBar.HORIZONTAL = 2;
8955
8956 /**
8957  * Placement constant - The resizing element is to the left of the splitter element
8958  * @static
8959  * @type Number
8960  */
8961 Roo.SplitBar.LEFT = 1;
8962
8963 /**
8964  * Placement constant - The resizing element is to the right of the splitter element
8965  * @static
8966  * @type Number
8967  */
8968 Roo.SplitBar.RIGHT = 2;
8969
8970 /**
8971  * Placement constant - The resizing element is positioned above the splitter element
8972  * @static
8973  * @type Number
8974  */
8975 Roo.SplitBar.TOP = 3;
8976
8977 /**
8978  * Placement constant - The resizing element is positioned under splitter element
8979  * @static
8980  * @type Number
8981  */
8982 Roo.SplitBar.BOTTOM = 4;
8983 /*
8984  * Based on:
8985  * Ext JS Library 1.1.1
8986  * Copyright(c) 2006-2007, Ext JS, LLC.
8987  *
8988  * Originally Released Under LGPL - original licence link has changed is not relivant.
8989  *
8990  * Fork - LGPL
8991  * <script type="text/javascript">
8992  */
8993
8994 /**
8995  * @class Roo.View
8996  * @extends Roo.util.Observable
8997  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8998  * This class also supports single and multi selection modes. <br>
8999  * Create a data model bound view:
9000  <pre><code>
9001  var store = new Roo.data.Store(...);
9002
9003  var view = new Roo.View({
9004     el : "my-element",
9005     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9006  
9007     singleSelect: true,
9008     selectedClass: "ydataview-selected",
9009     store: store
9010  });
9011
9012  // listen for node click?
9013  view.on("click", function(vw, index, node, e){
9014  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9015  });
9016
9017  // load XML data
9018  dataModel.load("foobar.xml");
9019  </code></pre>
9020  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9021  * <br><br>
9022  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9023  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9024  * 
9025  * Note: old style constructor is still suported (container, template, config)
9026  * 
9027  * @constructor
9028  * Create a new View
9029  * @param {Object} config The config object
9030  * 
9031  */
9032 Roo.View = function(config, depreciated_tpl, depreciated_config){
9033     
9034     if (typeof(depreciated_tpl) == 'undefined') {
9035         // new way.. - universal constructor.
9036         Roo.apply(this, config);
9037         this.el  = Roo.get(this.el);
9038     } else {
9039         // old format..
9040         this.el  = Roo.get(config);
9041         this.tpl = depreciated_tpl;
9042         Roo.apply(this, depreciated_config);
9043     }
9044      
9045     
9046     if(typeof(this.tpl) == "string"){
9047         this.tpl = new Roo.Template(this.tpl);
9048     } else {
9049         // support xtype ctors..
9050         this.tpl = new Roo.factory(this.tpl, Roo);
9051     }
9052     
9053     
9054     this.tpl.compile();
9055    
9056
9057      
9058     /** @private */
9059     this.addEvents({
9060     /**
9061      * @event beforeclick
9062      * Fires before a click is processed. Returns false to cancel the default action.
9063      * @param {Roo.View} this
9064      * @param {Number} index The index of the target node
9065      * @param {HTMLElement} node The target node
9066      * @param {Roo.EventObject} e The raw event object
9067      */
9068         "beforeclick" : true,
9069     /**
9070      * @event click
9071      * Fires when a template node is clicked.
9072      * @param {Roo.View} this
9073      * @param {Number} index The index of the target node
9074      * @param {HTMLElement} node The target node
9075      * @param {Roo.EventObject} e The raw event object
9076      */
9077         "click" : true,
9078     /**
9079      * @event dblclick
9080      * Fires when a template node is double clicked.
9081      * @param {Roo.View} this
9082      * @param {Number} index The index of the target node
9083      * @param {HTMLElement} node The target node
9084      * @param {Roo.EventObject} e The raw event object
9085      */
9086         "dblclick" : true,
9087     /**
9088      * @event contextmenu
9089      * Fires when a template node is right clicked.
9090      * @param {Roo.View} this
9091      * @param {Number} index The index of the target node
9092      * @param {HTMLElement} node The target node
9093      * @param {Roo.EventObject} e The raw event object
9094      */
9095         "contextmenu" : true,
9096     /**
9097      * @event selectionchange
9098      * Fires when the selected nodes change.
9099      * @param {Roo.View} this
9100      * @param {Array} selections Array of the selected nodes
9101      */
9102         "selectionchange" : true,
9103
9104     /**
9105      * @event beforeselect
9106      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9107      * @param {Roo.View} this
9108      * @param {HTMLElement} node The node to be selected
9109      * @param {Array} selections Array of currently selected nodes
9110      */
9111         "beforeselect" : true
9112     });
9113
9114     this.el.on({
9115         "click": this.onClick,
9116         "dblclick": this.onDblClick,
9117         "contextmenu": this.onContextMenu,
9118         scope:this
9119     });
9120
9121     this.selections = [];
9122     this.nodes = [];
9123     this.cmp = new Roo.CompositeElementLite([]);
9124     if(this.store){
9125         this.store = Roo.factory(this.store, Roo.data);
9126         this.setStore(this.store, true);
9127     }
9128     Roo.View.superclass.constructor.call(this);
9129 };
9130
9131 Roo.extend(Roo.View, Roo.util.Observable, {
9132     
9133      /**
9134      * @cfg {Roo.data.Store} store Data store to load data from.
9135      */
9136     store : false,
9137     
9138     /**
9139      * @cfg {String|Roo.Element} el The container element.
9140      */
9141     el : '',
9142     
9143     /**
9144      * @cfg {String|Roo.Template} tpl The template used by this View 
9145      */
9146     tpl : false,
9147     
9148     /**
9149      * @cfg {String} selectedClass The css class to add to selected nodes
9150      */
9151     selectedClass : "x-view-selected",
9152      /**
9153      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9154      */
9155     emptyText : "",
9156     /**
9157      * @cfg {Boolean} multiSelect Allow multiple selection
9158      */
9159     
9160     multiSelect : false,
9161     /**
9162      * @cfg {Boolean} singleSelect Allow single selection
9163      */
9164     singleSelect:  false,
9165     
9166     /**
9167      * Returns the element this view is bound to.
9168      * @return {Roo.Element}
9169      */
9170     getEl : function(){
9171         return this.el;
9172     },
9173
9174     /**
9175      * Refreshes the view.
9176      */
9177     refresh : function(){
9178         var t = this.tpl;
9179         this.clearSelections();
9180         this.el.update("");
9181         var html = [];
9182         var records = this.store.getRange();
9183         if(records.length < 1){
9184             this.el.update(this.emptyText);
9185             return;
9186         }
9187         for(var i = 0, len = records.length; i < len; i++){
9188             var data = this.prepareData(records[i].data, i, records[i]);
9189             html[html.length] = t.apply(data);
9190         }
9191         this.el.update(html.join(""));
9192         this.nodes = this.el.dom.childNodes;
9193         this.updateIndexes(0);
9194     },
9195
9196     /**
9197      * Function to override to reformat the data that is sent to
9198      * the template for each node.
9199      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9200      * a JSON object for an UpdateManager bound view).
9201      */
9202     prepareData : function(data){
9203         return data;
9204     },
9205
9206     onUpdate : function(ds, record){
9207         this.clearSelections();
9208         var index = this.store.indexOf(record);
9209         var n = this.nodes[index];
9210         this.tpl.insertBefore(n, this.prepareData(record.data));
9211         n.parentNode.removeChild(n);
9212         this.updateIndexes(index, index);
9213     },
9214
9215     onAdd : function(ds, records, index){
9216         this.clearSelections();
9217         if(this.nodes.length == 0){
9218             this.refresh();
9219             return;
9220         }
9221         var n = this.nodes[index];
9222         for(var i = 0, len = records.length; i < len; i++){
9223             var d = this.prepareData(records[i].data);
9224             if(n){
9225                 this.tpl.insertBefore(n, d);
9226             }else{
9227                 this.tpl.append(this.el, d);
9228             }
9229         }
9230         this.updateIndexes(index);
9231     },
9232
9233     onRemove : function(ds, record, index){
9234         this.clearSelections();
9235         this.el.dom.removeChild(this.nodes[index]);
9236         this.updateIndexes(index);
9237     },
9238
9239     /**
9240      * Refresh an individual node.
9241      * @param {Number} index
9242      */
9243     refreshNode : function(index){
9244         this.onUpdate(this.store, this.store.getAt(index));
9245     },
9246
9247     updateIndexes : function(startIndex, endIndex){
9248         var ns = this.nodes;
9249         startIndex = startIndex || 0;
9250         endIndex = endIndex || ns.length - 1;
9251         for(var i = startIndex; i <= endIndex; i++){
9252             ns[i].nodeIndex = i;
9253         }
9254     },
9255
9256     /**
9257      * Changes the data store this view uses and refresh the view.
9258      * @param {Store} store
9259      */
9260     setStore : function(store, initial){
9261         if(!initial && this.store){
9262             this.store.un("datachanged", this.refresh);
9263             this.store.un("add", this.onAdd);
9264             this.store.un("remove", this.onRemove);
9265             this.store.un("update", this.onUpdate);
9266             this.store.un("clear", this.refresh);
9267         }
9268         if(store){
9269           
9270             store.on("datachanged", this.refresh, this);
9271             store.on("add", this.onAdd, this);
9272             store.on("remove", this.onRemove, this);
9273             store.on("update", this.onUpdate, this);
9274             store.on("clear", this.refresh, this);
9275         }
9276         
9277         if(store){
9278             this.refresh();
9279         }
9280     },
9281
9282     /**
9283      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9284      * @param {HTMLElement} node
9285      * @return {HTMLElement} The template node
9286      */
9287     findItemFromChild : function(node){
9288         var el = this.el.dom;
9289         if(!node || node.parentNode == el){
9290                     return node;
9291             }
9292             var p = node.parentNode;
9293             while(p && p != el){
9294             if(p.parentNode == el){
9295                 return p;
9296             }
9297             p = p.parentNode;
9298         }
9299             return null;
9300     },
9301
9302     /** @ignore */
9303     onClick : function(e){
9304         var item = this.findItemFromChild(e.getTarget());
9305         if(item){
9306             var index = this.indexOf(item);
9307             if(this.onItemClick(item, index, e) !== false){
9308                 this.fireEvent("click", this, index, item, e);
9309             }
9310         }else{
9311             this.clearSelections();
9312         }
9313     },
9314
9315     /** @ignore */
9316     onContextMenu : function(e){
9317         var item = this.findItemFromChild(e.getTarget());
9318         if(item){
9319             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9320         }
9321     },
9322
9323     /** @ignore */
9324     onDblClick : function(e){
9325         var item = this.findItemFromChild(e.getTarget());
9326         if(item){
9327             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9328         }
9329     },
9330
9331     onItemClick : function(item, index, e){
9332         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9333             return false;
9334         }
9335         if(this.multiSelect || this.singleSelect){
9336             if(this.multiSelect && e.shiftKey && this.lastSelection){
9337                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9338             }else{
9339                 this.select(item, this.multiSelect && e.ctrlKey);
9340                 this.lastSelection = item;
9341             }
9342             e.preventDefault();
9343         }
9344         return true;
9345     },
9346
9347     /**
9348      * Get the number of selected nodes.
9349      * @return {Number}
9350      */
9351     getSelectionCount : function(){
9352         return this.selections.length;
9353     },
9354
9355     /**
9356      * Get the currently selected nodes.
9357      * @return {Array} An array of HTMLElements
9358      */
9359     getSelectedNodes : function(){
9360         return this.selections;
9361     },
9362
9363     /**
9364      * Get the indexes of the selected nodes.
9365      * @return {Array}
9366      */
9367     getSelectedIndexes : function(){
9368         var indexes = [], s = this.selections;
9369         for(var i = 0, len = s.length; i < len; i++){
9370             indexes.push(s[i].nodeIndex);
9371         }
9372         return indexes;
9373     },
9374
9375     /**
9376      * Clear all selections
9377      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9378      */
9379     clearSelections : function(suppressEvent){
9380         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9381             this.cmp.elements = this.selections;
9382             this.cmp.removeClass(this.selectedClass);
9383             this.selections = [];
9384             if(!suppressEvent){
9385                 this.fireEvent("selectionchange", this, this.selections);
9386             }
9387         }
9388     },
9389
9390     /**
9391      * Returns true if the passed node is selected
9392      * @param {HTMLElement/Number} node The node or node index
9393      * @return {Boolean}
9394      */
9395     isSelected : function(node){
9396         var s = this.selections;
9397         if(s.length < 1){
9398             return false;
9399         }
9400         node = this.getNode(node);
9401         return s.indexOf(node) !== -1;
9402     },
9403
9404     /**
9405      * Selects nodes.
9406      * @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
9407      * @param {Boolean} keepExisting (optional) true to keep existing selections
9408      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9409      */
9410     select : function(nodeInfo, keepExisting, suppressEvent){
9411         if(nodeInfo instanceof Array){
9412             if(!keepExisting){
9413                 this.clearSelections(true);
9414             }
9415             for(var i = 0, len = nodeInfo.length; i < len; i++){
9416                 this.select(nodeInfo[i], true, true);
9417             }
9418         } else{
9419             var node = this.getNode(nodeInfo);
9420             if(node && !this.isSelected(node)){
9421                 if(!keepExisting){
9422                     this.clearSelections(true);
9423                 }
9424                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9425                     Roo.fly(node).addClass(this.selectedClass);
9426                     this.selections.push(node);
9427                     if(!suppressEvent){
9428                         this.fireEvent("selectionchange", this, this.selections);
9429                     }
9430                 }
9431             }
9432         }
9433     },
9434
9435     /**
9436      * Gets a template node.
9437      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9438      * @return {HTMLElement} The node or null if it wasn't found
9439      */
9440     getNode : function(nodeInfo){
9441         if(typeof nodeInfo == "string"){
9442             return document.getElementById(nodeInfo);
9443         }else if(typeof nodeInfo == "number"){
9444             return this.nodes[nodeInfo];
9445         }
9446         return nodeInfo;
9447     },
9448
9449     /**
9450      * Gets a range template nodes.
9451      * @param {Number} startIndex
9452      * @param {Number} endIndex
9453      * @return {Array} An array of nodes
9454      */
9455     getNodes : function(start, end){
9456         var ns = this.nodes;
9457         start = start || 0;
9458         end = typeof end == "undefined" ? ns.length - 1 : end;
9459         var nodes = [];
9460         if(start <= end){
9461             for(var i = start; i <= end; i++){
9462                 nodes.push(ns[i]);
9463             }
9464         } else{
9465             for(var i = start; i >= end; i--){
9466                 nodes.push(ns[i]);
9467             }
9468         }
9469         return nodes;
9470     },
9471
9472     /**
9473      * Finds the index of the passed node
9474      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9475      * @return {Number} The index of the node or -1
9476      */
9477     indexOf : function(node){
9478         node = this.getNode(node);
9479         if(typeof node.nodeIndex == "number"){
9480             return node.nodeIndex;
9481         }
9482         var ns = this.nodes;
9483         for(var i = 0, len = ns.length; i < len; i++){
9484             if(ns[i] == node){
9485                 return i;
9486             }
9487         }
9488         return -1;
9489     }
9490 });
9491 /*
9492  * Based on:
9493  * Ext JS Library 1.1.1
9494  * Copyright(c) 2006-2007, Ext JS, LLC.
9495  *
9496  * Originally Released Under LGPL - original licence link has changed is not relivant.
9497  *
9498  * Fork - LGPL
9499  * <script type="text/javascript">
9500  */
9501
9502 /**
9503  * @class Roo.JsonView
9504  * @extends Roo.View
9505  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9506 <pre><code>
9507 var view = new Roo.JsonView({
9508     container: "my-element",
9509     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9510     multiSelect: true, 
9511     jsonRoot: "data" 
9512 });
9513
9514 // listen for node click?
9515 view.on("click", function(vw, index, node, e){
9516     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9517 });
9518
9519 // direct load of JSON data
9520 view.load("foobar.php");
9521
9522 // Example from my blog list
9523 var tpl = new Roo.Template(
9524     '&lt;div class="entry"&gt;' +
9525     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9526     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9527     "&lt;/div&gt;&lt;hr /&gt;"
9528 );
9529
9530 var moreView = new Roo.JsonView({
9531     container :  "entry-list", 
9532     template : tpl,
9533     jsonRoot: "posts"
9534 });
9535 moreView.on("beforerender", this.sortEntries, this);
9536 moreView.load({
9537     url: "/blog/get-posts.php",
9538     params: "allposts=true",
9539     text: "Loading Blog Entries..."
9540 });
9541 </code></pre>
9542
9543 * Note: old code is supported with arguments : (container, template, config)
9544
9545
9546  * @constructor
9547  * Create a new JsonView
9548  * 
9549  * @param {Object} config The config object
9550  * 
9551  */
9552 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9553     
9554     
9555     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9556
9557     var um = this.el.getUpdateManager();
9558     um.setRenderer(this);
9559     um.on("update", this.onLoad, this);
9560     um.on("failure", this.onLoadException, this);
9561
9562     /**
9563      * @event beforerender
9564      * Fires before rendering of the downloaded JSON data.
9565      * @param {Roo.JsonView} this
9566      * @param {Object} data The JSON data loaded
9567      */
9568     /**
9569      * @event load
9570      * Fires when data is loaded.
9571      * @param {Roo.JsonView} this
9572      * @param {Object} data The JSON data loaded
9573      * @param {Object} response The raw Connect response object
9574      */
9575     /**
9576      * @event loadexception
9577      * Fires when loading fails.
9578      * @param {Roo.JsonView} this
9579      * @param {Object} response The raw Connect response object
9580      */
9581     this.addEvents({
9582         'beforerender' : true,
9583         'load' : true,
9584         'loadexception' : true
9585     });
9586 };
9587 Roo.extend(Roo.JsonView, Roo.View, {
9588     /**
9589      * @type {String} The root property in the loaded JSON object that contains the data
9590      */
9591     jsonRoot : "",
9592
9593     /**
9594      * Refreshes the view.
9595      */
9596     refresh : function(){
9597         this.clearSelections();
9598         this.el.update("");
9599         var html = [];
9600         var o = this.jsonData;
9601         if(o && o.length > 0){
9602             for(var i = 0, len = o.length; i < len; i++){
9603                 var data = this.prepareData(o[i], i, o);
9604                 html[html.length] = this.tpl.apply(data);
9605             }
9606         }else{
9607             html.push(this.emptyText);
9608         }
9609         this.el.update(html.join(""));
9610         this.nodes = this.el.dom.childNodes;
9611         this.updateIndexes(0);
9612     },
9613
9614     /**
9615      * 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.
9616      * @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:
9617      <pre><code>
9618      view.load({
9619          url: "your-url.php",
9620          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9621          callback: yourFunction,
9622          scope: yourObject, //(optional scope)
9623          discardUrl: false,
9624          nocache: false,
9625          text: "Loading...",
9626          timeout: 30,
9627          scripts: false
9628      });
9629      </code></pre>
9630      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9631      * 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.
9632      * @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}
9633      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9634      * @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.
9635      */
9636     load : function(){
9637         var um = this.el.getUpdateManager();
9638         um.update.apply(um, arguments);
9639     },
9640
9641     render : function(el, response){
9642         this.clearSelections();
9643         this.el.update("");
9644         var o;
9645         try{
9646             o = Roo.util.JSON.decode(response.responseText);
9647             if(this.jsonRoot){
9648                 
9649                 o = o[this.jsonRoot];
9650             }
9651         } catch(e){
9652         }
9653         /**
9654          * The current JSON data or null
9655          */
9656         this.jsonData = o;
9657         this.beforeRender();
9658         this.refresh();
9659     },
9660
9661 /**
9662  * Get the number of records in the current JSON dataset
9663  * @return {Number}
9664  */
9665     getCount : function(){
9666         return this.jsonData ? this.jsonData.length : 0;
9667     },
9668
9669 /**
9670  * Returns the JSON object for the specified node(s)
9671  * @param {HTMLElement/Array} node The node or an array of nodes
9672  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9673  * you get the JSON object for the node
9674  */
9675     getNodeData : function(node){
9676         if(node instanceof Array){
9677             var data = [];
9678             for(var i = 0, len = node.length; i < len; i++){
9679                 data.push(this.getNodeData(node[i]));
9680             }
9681             return data;
9682         }
9683         return this.jsonData[this.indexOf(node)] || null;
9684     },
9685
9686     beforeRender : function(){
9687         this.snapshot = this.jsonData;
9688         if(this.sortInfo){
9689             this.sort.apply(this, this.sortInfo);
9690         }
9691         this.fireEvent("beforerender", this, this.jsonData);
9692     },
9693
9694     onLoad : function(el, o){
9695         this.fireEvent("load", this, this.jsonData, o);
9696     },
9697
9698     onLoadException : function(el, o){
9699         this.fireEvent("loadexception", this, o);
9700     },
9701
9702 /**
9703  * Filter the data by a specific property.
9704  * @param {String} property A property on your JSON objects
9705  * @param {String/RegExp} value Either string that the property values
9706  * should start with, or a RegExp to test against the property
9707  */
9708     filter : function(property, value){
9709         if(this.jsonData){
9710             var data = [];
9711             var ss = this.snapshot;
9712             if(typeof value == "string"){
9713                 var vlen = value.length;
9714                 if(vlen == 0){
9715                     this.clearFilter();
9716                     return;
9717                 }
9718                 value = value.toLowerCase();
9719                 for(var i = 0, len = ss.length; i < len; i++){
9720                     var o = ss[i];
9721                     if(o[property].substr(0, vlen).toLowerCase() == value){
9722                         data.push(o);
9723                     }
9724                 }
9725             } else if(value.exec){ // regex?
9726                 for(var i = 0, len = ss.length; i < len; i++){
9727                     var o = ss[i];
9728                     if(value.test(o[property])){
9729                         data.push(o);
9730                     }
9731                 }
9732             } else{
9733                 return;
9734             }
9735             this.jsonData = data;
9736             this.refresh();
9737         }
9738     },
9739
9740 /**
9741  * Filter by a function. The passed function will be called with each
9742  * object in the current dataset. If the function returns true the value is kept,
9743  * otherwise it is filtered.
9744  * @param {Function} fn
9745  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9746  */
9747     filterBy : function(fn, scope){
9748         if(this.jsonData){
9749             var data = [];
9750             var ss = this.snapshot;
9751             for(var i = 0, len = ss.length; i < len; i++){
9752                 var o = ss[i];
9753                 if(fn.call(scope || this, o)){
9754                     data.push(o);
9755                 }
9756             }
9757             this.jsonData = data;
9758             this.refresh();
9759         }
9760     },
9761
9762 /**
9763  * Clears the current filter.
9764  */
9765     clearFilter : function(){
9766         if(this.snapshot && this.jsonData != this.snapshot){
9767             this.jsonData = this.snapshot;
9768             this.refresh();
9769         }
9770     },
9771
9772
9773 /**
9774  * Sorts the data for this view and refreshes it.
9775  * @param {String} property A property on your JSON objects to sort on
9776  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9777  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9778  */
9779     sort : function(property, dir, sortType){
9780         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9781         if(this.jsonData){
9782             var p = property;
9783             var dsc = dir && dir.toLowerCase() == "desc";
9784             var f = function(o1, o2){
9785                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9786                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9787                 ;
9788                 if(v1 < v2){
9789                     return dsc ? +1 : -1;
9790                 } else if(v1 > v2){
9791                     return dsc ? -1 : +1;
9792                 } else{
9793                     return 0;
9794                 }
9795             };
9796             this.jsonData.sort(f);
9797             this.refresh();
9798             if(this.jsonData != this.snapshot){
9799                 this.snapshot.sort(f);
9800             }
9801         }
9802     }
9803 });/*
9804  * Based on:
9805  * Ext JS Library 1.1.1
9806  * Copyright(c) 2006-2007, Ext JS, LLC.
9807  *
9808  * Originally Released Under LGPL - original licence link has changed is not relivant.
9809  *
9810  * Fork - LGPL
9811  * <script type="text/javascript">
9812  */
9813  
9814
9815 /**
9816  * @class Roo.ColorPalette
9817  * @extends Roo.Component
9818  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9819  * Here's an example of typical usage:
9820  * <pre><code>
9821 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9822 cp.render('my-div');
9823
9824 cp.on('select', function(palette, selColor){
9825     // do something with selColor
9826 });
9827 </code></pre>
9828  * @constructor
9829  * Create a new ColorPalette
9830  * @param {Object} config The config object
9831  */
9832 Roo.ColorPalette = function(config){
9833     Roo.ColorPalette.superclass.constructor.call(this, config);
9834     this.addEvents({
9835         /**
9836              * @event select
9837              * Fires when a color is selected
9838              * @param {ColorPalette} this
9839              * @param {String} color The 6-digit color hex code (without the # symbol)
9840              */
9841         select: true
9842     });
9843
9844     if(this.handler){
9845         this.on("select", this.handler, this.scope, true);
9846     }
9847 };
9848 Roo.extend(Roo.ColorPalette, Roo.Component, {
9849     /**
9850      * @cfg {String} itemCls
9851      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9852      */
9853     itemCls : "x-color-palette",
9854     /**
9855      * @cfg {String} value
9856      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9857      * the hex codes are case-sensitive.
9858      */
9859     value : null,
9860     clickEvent:'click',
9861     // private
9862     ctype: "Roo.ColorPalette",
9863
9864     /**
9865      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9866      */
9867     allowReselect : false,
9868
9869     /**
9870      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9871      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9872      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9873      * of colors with the width setting until the box is symmetrical.</p>
9874      * <p>You can override individual colors if needed:</p>
9875      * <pre><code>
9876 var cp = new Roo.ColorPalette();
9877 cp.colors[0] = "FF0000";  // change the first box to red
9878 </code></pre>
9879
9880 Or you can provide a custom array of your own for complete control:
9881 <pre><code>
9882 var cp = new Roo.ColorPalette();
9883 cp.colors = ["000000", "993300", "333300"];
9884 </code></pre>
9885      * @type Array
9886      */
9887     colors : [
9888         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9889         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9890         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9891         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9892         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9893     ],
9894
9895     // private
9896     onRender : function(container, position){
9897         var t = new Roo.MasterTemplate(
9898             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9899         );
9900         var c = this.colors;
9901         for(var i = 0, len = c.length; i < len; i++){
9902             t.add([c[i]]);
9903         }
9904         var el = document.createElement("div");
9905         el.className = this.itemCls;
9906         t.overwrite(el);
9907         container.dom.insertBefore(el, position);
9908         this.el = Roo.get(el);
9909         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9910         if(this.clickEvent != 'click'){
9911             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9912         }
9913     },
9914
9915     // private
9916     afterRender : function(){
9917         Roo.ColorPalette.superclass.afterRender.call(this);
9918         if(this.value){
9919             var s = this.value;
9920             this.value = null;
9921             this.select(s);
9922         }
9923     },
9924
9925     // private
9926     handleClick : function(e, t){
9927         e.preventDefault();
9928         if(!this.disabled){
9929             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9930             this.select(c.toUpperCase());
9931         }
9932     },
9933
9934     /**
9935      * Selects the specified color in the palette (fires the select event)
9936      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9937      */
9938     select : function(color){
9939         color = color.replace("#", "");
9940         if(color != this.value || this.allowReselect){
9941             var el = this.el;
9942             if(this.value){
9943                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9944             }
9945             el.child("a.color-"+color).addClass("x-color-palette-sel");
9946             this.value = color;
9947             this.fireEvent("select", this, color);
9948         }
9949     }
9950 });/*
9951  * Based on:
9952  * Ext JS Library 1.1.1
9953  * Copyright(c) 2006-2007, Ext JS, LLC.
9954  *
9955  * Originally Released Under LGPL - original licence link has changed is not relivant.
9956  *
9957  * Fork - LGPL
9958  * <script type="text/javascript">
9959  */
9960  
9961 /**
9962  * @class Roo.DatePicker
9963  * @extends Roo.Component
9964  * Simple date picker class.
9965  * @constructor
9966  * Create a new DatePicker
9967  * @param {Object} config The config object
9968  */
9969 Roo.DatePicker = function(config){
9970     Roo.DatePicker.superclass.constructor.call(this, config);
9971
9972     this.value = config && config.value ?
9973                  config.value.clearTime() : new Date().clearTime();
9974
9975     this.addEvents({
9976         /**
9977              * @event select
9978              * Fires when a date is selected
9979              * @param {DatePicker} this
9980              * @param {Date} date The selected date
9981              */
9982         select: true
9983     });
9984
9985     if(this.handler){
9986         this.on("select", this.handler,  this.scope || this);
9987     }
9988     // build the disabledDatesRE
9989     if(!this.disabledDatesRE && this.disabledDates){
9990         var dd = this.disabledDates;
9991         var re = "(?:";
9992         for(var i = 0; i < dd.length; i++){
9993             re += dd[i];
9994             if(i != dd.length-1) re += "|";
9995         }
9996         this.disabledDatesRE = new RegExp(re + ")");
9997     }
9998 };
9999
10000 Roo.extend(Roo.DatePicker, Roo.Component, {
10001     /**
10002      * @cfg {String} todayText
10003      * The text to display on the button that selects the current date (defaults to "Today")
10004      */
10005     todayText : "Today",
10006     /**
10007      * @cfg {String} okText
10008      * The text to display on the ok button
10009      */
10010     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10011     /**
10012      * @cfg {String} cancelText
10013      * The text to display on the cancel button
10014      */
10015     cancelText : "Cancel",
10016     /**
10017      * @cfg {String} todayTip
10018      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10019      */
10020     todayTip : "{0} (Spacebar)",
10021     /**
10022      * @cfg {Date} minDate
10023      * Minimum allowable date (JavaScript date object, defaults to null)
10024      */
10025     minDate : null,
10026     /**
10027      * @cfg {Date} maxDate
10028      * Maximum allowable date (JavaScript date object, defaults to null)
10029      */
10030     maxDate : null,
10031     /**
10032      * @cfg {String} minText
10033      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10034      */
10035     minText : "This date is before the minimum date",
10036     /**
10037      * @cfg {String} maxText
10038      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10039      */
10040     maxText : "This date is after the maximum date",
10041     /**
10042      * @cfg {String} format
10043      * The default date format string which can be overriden for localization support.  The format must be
10044      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10045      */
10046     format : "m/d/y",
10047     /**
10048      * @cfg {Array} disabledDays
10049      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10050      */
10051     disabledDays : null,
10052     /**
10053      * @cfg {String} disabledDaysText
10054      * The tooltip to display when the date falls on a disabled day (defaults to "")
10055      */
10056     disabledDaysText : "",
10057     /**
10058      * @cfg {RegExp} disabledDatesRE
10059      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10060      */
10061     disabledDatesRE : null,
10062     /**
10063      * @cfg {String} disabledDatesText
10064      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10065      */
10066     disabledDatesText : "",
10067     /**
10068      * @cfg {Boolean} constrainToViewport
10069      * True to constrain the date picker to the viewport (defaults to true)
10070      */
10071     constrainToViewport : true,
10072     /**
10073      * @cfg {Array} monthNames
10074      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10075      */
10076     monthNames : Date.monthNames,
10077     /**
10078      * @cfg {Array} dayNames
10079      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10080      */
10081     dayNames : Date.dayNames,
10082     /**
10083      * @cfg {String} nextText
10084      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10085      */
10086     nextText: 'Next Month (Control+Right)',
10087     /**
10088      * @cfg {String} prevText
10089      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10090      */
10091     prevText: 'Previous Month (Control+Left)',
10092     /**
10093      * @cfg {String} monthYearText
10094      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10095      */
10096     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10097     /**
10098      * @cfg {Number} startDay
10099      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10100      */
10101     startDay : 0,
10102     /**
10103      * @cfg {Bool} showClear
10104      * Show a clear button (usefull for date form elements that can be blank.)
10105      */
10106     
10107     showClear: false,
10108     
10109     /**
10110      * Sets the value of the date field
10111      * @param {Date} value The date to set
10112      */
10113     setValue : function(value){
10114         var old = this.value;
10115         this.value = value.clearTime(true);
10116         if(this.el){
10117             this.update(this.value);
10118         }
10119     },
10120
10121     /**
10122      * Gets the current selected value of the date field
10123      * @return {Date} The selected date
10124      */
10125     getValue : function(){
10126         return this.value;
10127     },
10128
10129     // private
10130     focus : function(){
10131         if(this.el){
10132             this.update(this.activeDate);
10133         }
10134     },
10135
10136     // private
10137     onRender : function(container, position){
10138         var m = [
10139              '<table cellspacing="0">',
10140                 '<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>',
10141                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10142         var dn = this.dayNames;
10143         for(var i = 0; i < 7; i++){
10144             var d = this.startDay+i;
10145             if(d > 6){
10146                 d = d-7;
10147             }
10148             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10149         }
10150         m[m.length] = "</tr></thead><tbody><tr>";
10151         for(var i = 0; i < 42; i++) {
10152             if(i % 7 == 0 && i != 0){
10153                 m[m.length] = "</tr><tr>";
10154             }
10155             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10156         }
10157         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10158             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10159
10160         var el = document.createElement("div");
10161         el.className = "x-date-picker";
10162         el.innerHTML = m.join("");
10163
10164         container.dom.insertBefore(el, position);
10165
10166         this.el = Roo.get(el);
10167         this.eventEl = Roo.get(el.firstChild);
10168
10169         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10170             handler: this.showPrevMonth,
10171             scope: this,
10172             preventDefault:true,
10173             stopDefault:true
10174         });
10175
10176         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10177             handler: this.showNextMonth,
10178             scope: this,
10179             preventDefault:true,
10180             stopDefault:true
10181         });
10182
10183         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10184
10185         this.monthPicker = this.el.down('div.x-date-mp');
10186         this.monthPicker.enableDisplayMode('block');
10187         
10188         var kn = new Roo.KeyNav(this.eventEl, {
10189             "left" : function(e){
10190                 e.ctrlKey ?
10191                     this.showPrevMonth() :
10192                     this.update(this.activeDate.add("d", -1));
10193             },
10194
10195             "right" : function(e){
10196                 e.ctrlKey ?
10197                     this.showNextMonth() :
10198                     this.update(this.activeDate.add("d", 1));
10199             },
10200
10201             "up" : function(e){
10202                 e.ctrlKey ?
10203                     this.showNextYear() :
10204                     this.update(this.activeDate.add("d", -7));
10205             },
10206
10207             "down" : function(e){
10208                 e.ctrlKey ?
10209                     this.showPrevYear() :
10210                     this.update(this.activeDate.add("d", 7));
10211             },
10212
10213             "pageUp" : function(e){
10214                 this.showNextMonth();
10215             },
10216
10217             "pageDown" : function(e){
10218                 this.showPrevMonth();
10219             },
10220
10221             "enter" : function(e){
10222                 e.stopPropagation();
10223                 return true;
10224             },
10225
10226             scope : this
10227         });
10228
10229         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10230
10231         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10232
10233         this.el.unselectable();
10234         
10235         this.cells = this.el.select("table.x-date-inner tbody td");
10236         this.textNodes = this.el.query("table.x-date-inner tbody span");
10237
10238         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10239             text: "&#160;",
10240             tooltip: this.monthYearText
10241         });
10242
10243         this.mbtn.on('click', this.showMonthPicker, this);
10244         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10245
10246
10247         var today = (new Date()).dateFormat(this.format);
10248         
10249         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10250         if (this.showClear) {
10251             baseTb.add( new Roo.Toolbar.Fill());
10252         }
10253         baseTb.add({
10254             text: String.format(this.todayText, today),
10255             tooltip: String.format(this.todayTip, today),
10256             handler: this.selectToday,
10257             scope: this
10258         });
10259         
10260         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10261             
10262         //});
10263         if (this.showClear) {
10264             
10265             baseTb.add( new Roo.Toolbar.Fill());
10266             baseTb.add({
10267                 text: '&#160;',
10268                 cls: 'x-btn-icon x-btn-clear',
10269                 handler: function() {
10270                     //this.value = '';
10271                     this.fireEvent("select", this, '');
10272                 },
10273                 scope: this
10274             });
10275         }
10276         
10277         
10278         if(Roo.isIE){
10279             this.el.repaint();
10280         }
10281         this.update(this.value);
10282     },
10283
10284     createMonthPicker : function(){
10285         if(!this.monthPicker.dom.firstChild){
10286             var buf = ['<table border="0" cellspacing="0">'];
10287             for(var i = 0; i < 6; i++){
10288                 buf.push(
10289                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10290                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10291                     i == 0 ?
10292                     '<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>' :
10293                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10294                 );
10295             }
10296             buf.push(
10297                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10298                     this.okText,
10299                     '</button><button type="button" class="x-date-mp-cancel">',
10300                     this.cancelText,
10301                     '</button></td></tr>',
10302                 '</table>'
10303             );
10304             this.monthPicker.update(buf.join(''));
10305             this.monthPicker.on('click', this.onMonthClick, this);
10306             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10307
10308             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10309             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10310
10311             this.mpMonths.each(function(m, a, i){
10312                 i += 1;
10313                 if((i%2) == 0){
10314                     m.dom.xmonth = 5 + Math.round(i * .5);
10315                 }else{
10316                     m.dom.xmonth = Math.round((i-1) * .5);
10317                 }
10318             });
10319         }
10320     },
10321
10322     showMonthPicker : function(){
10323         this.createMonthPicker();
10324         var size = this.el.getSize();
10325         this.monthPicker.setSize(size);
10326         this.monthPicker.child('table').setSize(size);
10327
10328         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10329         this.updateMPMonth(this.mpSelMonth);
10330         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10331         this.updateMPYear(this.mpSelYear);
10332
10333         this.monthPicker.slideIn('t', {duration:.2});
10334     },
10335
10336     updateMPYear : function(y){
10337         this.mpyear = y;
10338         var ys = this.mpYears.elements;
10339         for(var i = 1; i <= 10; i++){
10340             var td = ys[i-1], y2;
10341             if((i%2) == 0){
10342                 y2 = y + Math.round(i * .5);
10343                 td.firstChild.innerHTML = y2;
10344                 td.xyear = y2;
10345             }else{
10346                 y2 = y - (5-Math.round(i * .5));
10347                 td.firstChild.innerHTML = y2;
10348                 td.xyear = y2;
10349             }
10350             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10351         }
10352     },
10353
10354     updateMPMonth : function(sm){
10355         this.mpMonths.each(function(m, a, i){
10356             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10357         });
10358     },
10359
10360     selectMPMonth: function(m){
10361         
10362     },
10363
10364     onMonthClick : function(e, t){
10365         e.stopEvent();
10366         var el = new Roo.Element(t), pn;
10367         if(el.is('button.x-date-mp-cancel')){
10368             this.hideMonthPicker();
10369         }
10370         else if(el.is('button.x-date-mp-ok')){
10371             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10372             this.hideMonthPicker();
10373         }
10374         else if(pn = el.up('td.x-date-mp-month', 2)){
10375             this.mpMonths.removeClass('x-date-mp-sel');
10376             pn.addClass('x-date-mp-sel');
10377             this.mpSelMonth = pn.dom.xmonth;
10378         }
10379         else if(pn = el.up('td.x-date-mp-year', 2)){
10380             this.mpYears.removeClass('x-date-mp-sel');
10381             pn.addClass('x-date-mp-sel');
10382             this.mpSelYear = pn.dom.xyear;
10383         }
10384         else if(el.is('a.x-date-mp-prev')){
10385             this.updateMPYear(this.mpyear-10);
10386         }
10387         else if(el.is('a.x-date-mp-next')){
10388             this.updateMPYear(this.mpyear+10);
10389         }
10390     },
10391
10392     onMonthDblClick : function(e, t){
10393         e.stopEvent();
10394         var el = new Roo.Element(t), pn;
10395         if(pn = el.up('td.x-date-mp-month', 2)){
10396             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10397             this.hideMonthPicker();
10398         }
10399         else if(pn = el.up('td.x-date-mp-year', 2)){
10400             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10401             this.hideMonthPicker();
10402         }
10403     },
10404
10405     hideMonthPicker : function(disableAnim){
10406         if(this.monthPicker){
10407             if(disableAnim === true){
10408                 this.monthPicker.hide();
10409             }else{
10410                 this.monthPicker.slideOut('t', {duration:.2});
10411             }
10412         }
10413     },
10414
10415     // private
10416     showPrevMonth : function(e){
10417         this.update(this.activeDate.add("mo", -1));
10418     },
10419
10420     // private
10421     showNextMonth : function(e){
10422         this.update(this.activeDate.add("mo", 1));
10423     },
10424
10425     // private
10426     showPrevYear : function(){
10427         this.update(this.activeDate.add("y", -1));
10428     },
10429
10430     // private
10431     showNextYear : function(){
10432         this.update(this.activeDate.add("y", 1));
10433     },
10434
10435     // private
10436     handleMouseWheel : function(e){
10437         var delta = e.getWheelDelta();
10438         if(delta > 0){
10439             this.showPrevMonth();
10440             e.stopEvent();
10441         } else if(delta < 0){
10442             this.showNextMonth();
10443             e.stopEvent();
10444         }
10445     },
10446
10447     // private
10448     handleDateClick : function(e, t){
10449         e.stopEvent();
10450         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10451             this.setValue(new Date(t.dateValue));
10452             this.fireEvent("select", this, this.value);
10453         }
10454     },
10455
10456     // private
10457     selectToday : function(){
10458         this.setValue(new Date().clearTime());
10459         this.fireEvent("select", this, this.value);
10460     },
10461
10462     // private
10463     update : function(date){
10464         var vd = this.activeDate;
10465         this.activeDate = date;
10466         if(vd && this.el){
10467             var t = date.getTime();
10468             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10469                 this.cells.removeClass("x-date-selected");
10470                 this.cells.each(function(c){
10471                    if(c.dom.firstChild.dateValue == t){
10472                        c.addClass("x-date-selected");
10473                        setTimeout(function(){
10474                             try{c.dom.firstChild.focus();}catch(e){}
10475                        }, 50);
10476                        return false;
10477                    }
10478                 });
10479                 return;
10480             }
10481         }
10482         var days = date.getDaysInMonth();
10483         var firstOfMonth = date.getFirstDateOfMonth();
10484         var startingPos = firstOfMonth.getDay()-this.startDay;
10485
10486         if(startingPos <= this.startDay){
10487             startingPos += 7;
10488         }
10489
10490         var pm = date.add("mo", -1);
10491         var prevStart = pm.getDaysInMonth()-startingPos;
10492
10493         var cells = this.cells.elements;
10494         var textEls = this.textNodes;
10495         days += startingPos;
10496
10497         // convert everything to numbers so it's fast
10498         var day = 86400000;
10499         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10500         var today = new Date().clearTime().getTime();
10501         var sel = date.clearTime().getTime();
10502         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10503         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10504         var ddMatch = this.disabledDatesRE;
10505         var ddText = this.disabledDatesText;
10506         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10507         var ddaysText = this.disabledDaysText;
10508         var format = this.format;
10509
10510         var setCellClass = function(cal, cell){
10511             cell.title = "";
10512             var t = d.getTime();
10513             cell.firstChild.dateValue = t;
10514             if(t == today){
10515                 cell.className += " x-date-today";
10516                 cell.title = cal.todayText;
10517             }
10518             if(t == sel){
10519                 cell.className += " x-date-selected";
10520                 setTimeout(function(){
10521                     try{cell.firstChild.focus();}catch(e){}
10522                 }, 50);
10523             }
10524             // disabling
10525             if(t < min) {
10526                 cell.className = " x-date-disabled";
10527                 cell.title = cal.minText;
10528                 return;
10529             }
10530             if(t > max) {
10531                 cell.className = " x-date-disabled";
10532                 cell.title = cal.maxText;
10533                 return;
10534             }
10535             if(ddays){
10536                 if(ddays.indexOf(d.getDay()) != -1){
10537                     cell.title = ddaysText;
10538                     cell.className = " x-date-disabled";
10539                 }
10540             }
10541             if(ddMatch && format){
10542                 var fvalue = d.dateFormat(format);
10543                 if(ddMatch.test(fvalue)){
10544                     cell.title = ddText.replace("%0", fvalue);
10545                     cell.className = " x-date-disabled";
10546                 }
10547             }
10548         };
10549
10550         var i = 0;
10551         for(; i < startingPos; i++) {
10552             textEls[i].innerHTML = (++prevStart);
10553             d.setDate(d.getDate()+1);
10554             cells[i].className = "x-date-prevday";
10555             setCellClass(this, cells[i]);
10556         }
10557         for(; i < days; i++){
10558             intDay = i - startingPos + 1;
10559             textEls[i].innerHTML = (intDay);
10560             d.setDate(d.getDate()+1);
10561             cells[i].className = "x-date-active";
10562             setCellClass(this, cells[i]);
10563         }
10564         var extraDays = 0;
10565         for(; i < 42; i++) {
10566              textEls[i].innerHTML = (++extraDays);
10567              d.setDate(d.getDate()+1);
10568              cells[i].className = "x-date-nextday";
10569              setCellClass(this, cells[i]);
10570         }
10571
10572         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10573
10574         if(!this.internalRender){
10575             var main = this.el.dom.firstChild;
10576             var w = main.offsetWidth;
10577             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10578             Roo.fly(main).setWidth(w);
10579             this.internalRender = true;
10580             // opera does not respect the auto grow header center column
10581             // then, after it gets a width opera refuses to recalculate
10582             // without a second pass
10583             if(Roo.isOpera && !this.secondPass){
10584                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10585                 this.secondPass = true;
10586                 this.update.defer(10, this, [date]);
10587             }
10588         }
10589     }
10590 });/*
10591  * Based on:
10592  * Ext JS Library 1.1.1
10593  * Copyright(c) 2006-2007, Ext JS, LLC.
10594  *
10595  * Originally Released Under LGPL - original licence link has changed is not relivant.
10596  *
10597  * Fork - LGPL
10598  * <script type="text/javascript">
10599  */
10600 /**
10601  * @class Roo.TabPanel
10602  * @extends Roo.util.Observable
10603  * A lightweight tab container.
10604  * <br><br>
10605  * Usage:
10606  * <pre><code>
10607 // basic tabs 1, built from existing content
10608 var tabs = new Roo.TabPanel("tabs1");
10609 tabs.addTab("script", "View Script");
10610 tabs.addTab("markup", "View Markup");
10611 tabs.activate("script");
10612
10613 // more advanced tabs, built from javascript
10614 var jtabs = new Roo.TabPanel("jtabs");
10615 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10616
10617 // set up the UpdateManager
10618 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10619 var updater = tab2.getUpdateManager();
10620 updater.setDefaultUrl("ajax1.htm");
10621 tab2.on('activate', updater.refresh, updater, true);
10622
10623 // Use setUrl for Ajax loading
10624 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10625 tab3.setUrl("ajax2.htm", null, true);
10626
10627 // Disabled tab
10628 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10629 tab4.disable();
10630
10631 jtabs.activate("jtabs-1");
10632  * </code></pre>
10633  * @constructor
10634  * Create a new TabPanel.
10635  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10636  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10637  */
10638 Roo.TabPanel = function(container, config){
10639     /**
10640     * The container element for this TabPanel.
10641     * @type Roo.Element
10642     */
10643     this.el = Roo.get(container, true);
10644     if(config){
10645         if(typeof config == "boolean"){
10646             this.tabPosition = config ? "bottom" : "top";
10647         }else{
10648             Roo.apply(this, config);
10649         }
10650     }
10651     if(this.tabPosition == "bottom"){
10652         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10653         this.el.addClass("x-tabs-bottom");
10654     }
10655     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10656     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10657     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10658     if(Roo.isIE){
10659         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10660     }
10661     if(this.tabPosition != "bottom"){
10662     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10663      * @type Roo.Element
10664      */
10665       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10666       this.el.addClass("x-tabs-top");
10667     }
10668     this.items = [];
10669
10670     this.bodyEl.setStyle("position", "relative");
10671
10672     this.active = null;
10673     this.activateDelegate = this.activate.createDelegate(this);
10674
10675     this.addEvents({
10676         /**
10677          * @event tabchange
10678          * Fires when the active tab changes
10679          * @param {Roo.TabPanel} this
10680          * @param {Roo.TabPanelItem} activePanel The new active tab
10681          */
10682         "tabchange": true,
10683         /**
10684          * @event beforetabchange
10685          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10686          * @param {Roo.TabPanel} this
10687          * @param {Object} e Set cancel to true on this object to cancel the tab change
10688          * @param {Roo.TabPanelItem} tab The tab being changed to
10689          */
10690         "beforetabchange" : true
10691     });
10692
10693     Roo.EventManager.onWindowResize(this.onResize, this);
10694     this.cpad = this.el.getPadding("lr");
10695     this.hiddenCount = 0;
10696
10697     Roo.TabPanel.superclass.constructor.call(this);
10698 };
10699
10700 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10701         /*
10702          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10703          */
10704     tabPosition : "top",
10705         /*
10706          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10707          */
10708     currentTabWidth : 0,
10709         /*
10710          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10711          */
10712     minTabWidth : 40,
10713         /*
10714          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10715          */
10716     maxTabWidth : 250,
10717         /*
10718          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10719          */
10720     preferredTabWidth : 175,
10721         /*
10722          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10723          */
10724     resizeTabs : false,
10725         /*
10726          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10727          */
10728     monitorResize : true,
10729
10730     /**
10731      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10732      * @param {String} id The id of the div to use <b>or create</b>
10733      * @param {String} text The text for the tab
10734      * @param {String} content (optional) Content to put in the TabPanelItem body
10735      * @param {Boolean} closable (optional) True to create a close icon on the tab
10736      * @return {Roo.TabPanelItem} The created TabPanelItem
10737      */
10738     addTab : function(id, text, content, closable){
10739         var item = new Roo.TabPanelItem(this, id, text, closable);
10740         this.addTabItem(item);
10741         if(content){
10742             item.setContent(content);
10743         }
10744         return item;
10745     },
10746
10747     /**
10748      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10749      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10750      * @return {Roo.TabPanelItem}
10751      */
10752     getTab : function(id){
10753         return this.items[id];
10754     },
10755
10756     /**
10757      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10758      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10759      */
10760     hideTab : function(id){
10761         var t = this.items[id];
10762         if(!t.isHidden()){
10763            t.setHidden(true);
10764            this.hiddenCount++;
10765            this.autoSizeTabs();
10766         }
10767     },
10768
10769     /**
10770      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10771      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10772      */
10773     unhideTab : function(id){
10774         var t = this.items[id];
10775         if(t.isHidden()){
10776            t.setHidden(false);
10777            this.hiddenCount--;
10778            this.autoSizeTabs();
10779         }
10780     },
10781
10782     /**
10783      * Adds an existing {@link Roo.TabPanelItem}.
10784      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10785      */
10786     addTabItem : function(item){
10787         this.items[item.id] = item;
10788         this.items.push(item);
10789         if(this.resizeTabs){
10790            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10791            this.autoSizeTabs();
10792         }else{
10793             item.autoSize();
10794         }
10795     },
10796
10797     /**
10798      * Removes a {@link Roo.TabPanelItem}.
10799      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10800      */
10801     removeTab : function(id){
10802         var items = this.items;
10803         var tab = items[id];
10804         if(!tab) { return; }
10805         var index = items.indexOf(tab);
10806         if(this.active == tab && items.length > 1){
10807             var newTab = this.getNextAvailable(index);
10808             if(newTab) {
10809                 newTab.activate();
10810             }
10811         }
10812         this.stripEl.dom.removeChild(tab.pnode.dom);
10813         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10814             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10815         }
10816         items.splice(index, 1);
10817         delete this.items[tab.id];
10818         tab.fireEvent("close", tab);
10819         tab.purgeListeners();
10820         this.autoSizeTabs();
10821     },
10822
10823     getNextAvailable : function(start){
10824         var items = this.items;
10825         var index = start;
10826         // look for a next tab that will slide over to
10827         // replace the one being removed
10828         while(index < items.length){
10829             var item = items[++index];
10830             if(item && !item.isHidden()){
10831                 return item;
10832             }
10833         }
10834         // if one isn't found select the previous tab (on the left)
10835         index = start;
10836         while(index >= 0){
10837             var item = items[--index];
10838             if(item && !item.isHidden()){
10839                 return item;
10840             }
10841         }
10842         return null;
10843     },
10844
10845     /**
10846      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10847      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10848      */
10849     disableTab : function(id){
10850         var tab = this.items[id];
10851         if(tab && this.active != tab){
10852             tab.disable();
10853         }
10854     },
10855
10856     /**
10857      * Enables a {@link Roo.TabPanelItem} that is disabled.
10858      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10859      */
10860     enableTab : function(id){
10861         var tab = this.items[id];
10862         tab.enable();
10863     },
10864
10865     /**
10866      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10867      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10868      * @return {Roo.TabPanelItem} The TabPanelItem.
10869      */
10870     activate : function(id){
10871         var tab = this.items[id];
10872         if(!tab){
10873             return null;
10874         }
10875         if(tab == this.active || tab.disabled){
10876             return tab;
10877         }
10878         var e = {};
10879         this.fireEvent("beforetabchange", this, e, tab);
10880         if(e.cancel !== true && !tab.disabled){
10881             if(this.active){
10882                 this.active.hide();
10883             }
10884             this.active = this.items[id];
10885             this.active.show();
10886             this.fireEvent("tabchange", this, this.active);
10887         }
10888         return tab;
10889     },
10890
10891     /**
10892      * Gets the active {@link Roo.TabPanelItem}.
10893      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10894      */
10895     getActiveTab : function(){
10896         return this.active;
10897     },
10898
10899     /**
10900      * Updates the tab body element to fit the height of the container element
10901      * for overflow scrolling
10902      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10903      */
10904     syncHeight : function(targetHeight){
10905         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10906         var bm = this.bodyEl.getMargins();
10907         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10908         this.bodyEl.setHeight(newHeight);
10909         return newHeight;
10910     },
10911
10912     onResize : function(){
10913         if(this.monitorResize){
10914             this.autoSizeTabs();
10915         }
10916     },
10917
10918     /**
10919      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10920      */
10921     beginUpdate : function(){
10922         this.updating = true;
10923     },
10924
10925     /**
10926      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10927      */
10928     endUpdate : function(){
10929         this.updating = false;
10930         this.autoSizeTabs();
10931     },
10932
10933     /**
10934      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10935      */
10936     autoSizeTabs : function(){
10937         var count = this.items.length;
10938         var vcount = count - this.hiddenCount;
10939         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10940         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10941         var availWidth = Math.floor(w / vcount);
10942         var b = this.stripBody;
10943         if(b.getWidth() > w){
10944             var tabs = this.items;
10945             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10946             if(availWidth < this.minTabWidth){
10947                 /*if(!this.sleft){    // incomplete scrolling code
10948                     this.createScrollButtons();
10949                 }
10950                 this.showScroll();
10951                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10952             }
10953         }else{
10954             if(this.currentTabWidth < this.preferredTabWidth){
10955                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10956             }
10957         }
10958     },
10959
10960     /**
10961      * Returns the number of tabs in this TabPanel.
10962      * @return {Number}
10963      */
10964      getCount : function(){
10965          return this.items.length;
10966      },
10967
10968     /**
10969      * Resizes all the tabs to the passed width
10970      * @param {Number} The new width
10971      */
10972     setTabWidth : function(width){
10973         this.currentTabWidth = width;
10974         for(var i = 0, len = this.items.length; i < len; i++) {
10975                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10976         }
10977     },
10978
10979     /**
10980      * Destroys this TabPanel
10981      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10982      */
10983     destroy : function(removeEl){
10984         Roo.EventManager.removeResizeListener(this.onResize, this);
10985         for(var i = 0, len = this.items.length; i < len; i++){
10986             this.items[i].purgeListeners();
10987         }
10988         if(removeEl === true){
10989             this.el.update("");
10990             this.el.remove();
10991         }
10992     }
10993 });
10994
10995 /**
10996  * @class Roo.TabPanelItem
10997  * @extends Roo.util.Observable
10998  * Represents an individual item (tab plus body) in a TabPanel.
10999  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11000  * @param {String} id The id of this TabPanelItem
11001  * @param {String} text The text for the tab of this TabPanelItem
11002  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11003  */
11004 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11005     /**
11006      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11007      * @type Roo.TabPanel
11008      */
11009     this.tabPanel = tabPanel;
11010     /**
11011      * The id for this TabPanelItem
11012      * @type String
11013      */
11014     this.id = id;
11015     /** @private */
11016     this.disabled = false;
11017     /** @private */
11018     this.text = text;
11019     /** @private */
11020     this.loaded = false;
11021     this.closable = closable;
11022
11023     /**
11024      * The body element for this TabPanelItem.
11025      * @type Roo.Element
11026      */
11027     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11028     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11029     this.bodyEl.setStyle("display", "block");
11030     this.bodyEl.setStyle("zoom", "1");
11031     this.hideAction();
11032
11033     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11034     /** @private */
11035     this.el = Roo.get(els.el, true);
11036     this.inner = Roo.get(els.inner, true);
11037     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11038     this.pnode = Roo.get(els.el.parentNode, true);
11039     this.el.on("mousedown", this.onTabMouseDown, this);
11040     this.el.on("click", this.onTabClick, this);
11041     /** @private */
11042     if(closable){
11043         var c = Roo.get(els.close, true);
11044         c.dom.title = this.closeText;
11045         c.addClassOnOver("close-over");
11046         c.on("click", this.closeClick, this);
11047      }
11048
11049     this.addEvents({
11050          /**
11051          * @event activate
11052          * Fires when this tab becomes the active tab.
11053          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11054          * @param {Roo.TabPanelItem} this
11055          */
11056         "activate": true,
11057         /**
11058          * @event beforeclose
11059          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11060          * @param {Roo.TabPanelItem} this
11061          * @param {Object} e Set cancel to true on this object to cancel the close.
11062          */
11063         "beforeclose": true,
11064         /**
11065          * @event close
11066          * Fires when this tab is closed.
11067          * @param {Roo.TabPanelItem} this
11068          */
11069          "close": true,
11070         /**
11071          * @event deactivate
11072          * Fires when this tab is no longer the active tab.
11073          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11074          * @param {Roo.TabPanelItem} this
11075          */
11076          "deactivate" : true
11077     });
11078     this.hidden = false;
11079
11080     Roo.TabPanelItem.superclass.constructor.call(this);
11081 };
11082
11083 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11084     purgeListeners : function(){
11085        Roo.util.Observable.prototype.purgeListeners.call(this);
11086        this.el.removeAllListeners();
11087     },
11088     /**
11089      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11090      */
11091     show : function(){
11092         this.pnode.addClass("on");
11093         this.showAction();
11094         if(Roo.isOpera){
11095             this.tabPanel.stripWrap.repaint();
11096         }
11097         this.fireEvent("activate", this.tabPanel, this);
11098     },
11099
11100     /**
11101      * Returns true if this tab is the active tab.
11102      * @return {Boolean}
11103      */
11104     isActive : function(){
11105         return this.tabPanel.getActiveTab() == this;
11106     },
11107
11108     /**
11109      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11110      */
11111     hide : function(){
11112         this.pnode.removeClass("on");
11113         this.hideAction();
11114         this.fireEvent("deactivate", this.tabPanel, this);
11115     },
11116
11117     hideAction : function(){
11118         this.bodyEl.hide();
11119         this.bodyEl.setStyle("position", "absolute");
11120         this.bodyEl.setLeft("-20000px");
11121         this.bodyEl.setTop("-20000px");
11122     },
11123
11124     showAction : function(){
11125         this.bodyEl.setStyle("position", "relative");
11126         this.bodyEl.setTop("");
11127         this.bodyEl.setLeft("");
11128         this.bodyEl.show();
11129     },
11130
11131     /**
11132      * Set the tooltip for the tab.
11133      * @param {String} tooltip The tab's tooltip
11134      */
11135     setTooltip : function(text){
11136         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11137             this.textEl.dom.qtip = text;
11138             this.textEl.dom.removeAttribute('title');
11139         }else{
11140             this.textEl.dom.title = text;
11141         }
11142     },
11143
11144     onTabClick : function(e){
11145         e.preventDefault();
11146         this.tabPanel.activate(this.id);
11147     },
11148
11149     onTabMouseDown : function(e){
11150         e.preventDefault();
11151         this.tabPanel.activate(this.id);
11152     },
11153
11154     getWidth : function(){
11155         return this.inner.getWidth();
11156     },
11157
11158     setWidth : function(width){
11159         var iwidth = width - this.pnode.getPadding("lr");
11160         this.inner.setWidth(iwidth);
11161         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11162         this.pnode.setWidth(width);
11163     },
11164
11165     /**
11166      * Show or hide the tab
11167      * @param {Boolean} hidden True to hide or false to show.
11168      */
11169     setHidden : function(hidden){
11170         this.hidden = hidden;
11171         this.pnode.setStyle("display", hidden ? "none" : "");
11172     },
11173
11174     /**
11175      * Returns true if this tab is "hidden"
11176      * @return {Boolean}
11177      */
11178     isHidden : function(){
11179         return this.hidden;
11180     },
11181
11182     /**
11183      * Returns the text for this tab
11184      * @return {String}
11185      */
11186     getText : function(){
11187         return this.text;
11188     },
11189
11190     autoSize : function(){
11191         //this.el.beginMeasure();
11192         this.textEl.setWidth(1);
11193         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11194         //this.el.endMeasure();
11195     },
11196
11197     /**
11198      * Sets the text for the tab (Note: this also sets the tooltip text)
11199      * @param {String} text The tab's text and tooltip
11200      */
11201     setText : function(text){
11202         this.text = text;
11203         this.textEl.update(text);
11204         this.setTooltip(text);
11205         if(!this.tabPanel.resizeTabs){
11206             this.autoSize();
11207         }
11208     },
11209     /**
11210      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11211      */
11212     activate : function(){
11213         this.tabPanel.activate(this.id);
11214     },
11215
11216     /**
11217      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11218      */
11219     disable : function(){
11220         if(this.tabPanel.active != this){
11221             this.disabled = true;
11222             this.pnode.addClass("disabled");
11223         }
11224     },
11225
11226     /**
11227      * Enables this TabPanelItem if it was previously disabled.
11228      */
11229     enable : function(){
11230         this.disabled = false;
11231         this.pnode.removeClass("disabled");
11232     },
11233
11234     /**
11235      * Sets the content for this TabPanelItem.
11236      * @param {String} content The content
11237      * @param {Boolean} loadScripts true to look for and load scripts
11238      */
11239     setContent : function(content, loadScripts){
11240         this.bodyEl.update(content, loadScripts);
11241     },
11242
11243     /**
11244      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11245      * @return {Roo.UpdateManager} The UpdateManager
11246      */
11247     getUpdateManager : function(){
11248         return this.bodyEl.getUpdateManager();
11249     },
11250
11251     /**
11252      * Set a URL to be used to load the content for this TabPanelItem.
11253      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11254      * @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)
11255      * @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)
11256      * @return {Roo.UpdateManager} The UpdateManager
11257      */
11258     setUrl : function(url, params, loadOnce){
11259         if(this.refreshDelegate){
11260             this.un('activate', this.refreshDelegate);
11261         }
11262         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11263         this.on("activate", this.refreshDelegate);
11264         return this.bodyEl.getUpdateManager();
11265     },
11266
11267     /** @private */
11268     _handleRefresh : function(url, params, loadOnce){
11269         if(!loadOnce || !this.loaded){
11270             var updater = this.bodyEl.getUpdateManager();
11271             updater.update(url, params, this._setLoaded.createDelegate(this));
11272         }
11273     },
11274
11275     /**
11276      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11277      *   Will fail silently if the setUrl method has not been called.
11278      *   This does not activate the panel, just updates its content.
11279      */
11280     refresh : function(){
11281         if(this.refreshDelegate){
11282            this.loaded = false;
11283            this.refreshDelegate();
11284         }
11285     },
11286
11287     /** @private */
11288     _setLoaded : function(){
11289         this.loaded = true;
11290     },
11291
11292     /** @private */
11293     closeClick : function(e){
11294         var o = {};
11295         e.stopEvent();
11296         this.fireEvent("beforeclose", this, o);
11297         if(o.cancel !== true){
11298             this.tabPanel.removeTab(this.id);
11299         }
11300     },
11301     /**
11302      * The text displayed in the tooltip for the close icon.
11303      * @type String
11304      */
11305     closeText : "Close this tab"
11306 });
11307
11308 /** @private */
11309 Roo.TabPanel.prototype.createStrip = function(container){
11310     var strip = document.createElement("div");
11311     strip.className = "x-tabs-wrap";
11312     container.appendChild(strip);
11313     return strip;
11314 };
11315 /** @private */
11316 Roo.TabPanel.prototype.createStripList = function(strip){
11317     // div wrapper for retard IE
11318     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11319     return strip.firstChild.firstChild.firstChild.firstChild;
11320 };
11321 /** @private */
11322 Roo.TabPanel.prototype.createBody = function(container){
11323     var body = document.createElement("div");
11324     Roo.id(body, "tab-body");
11325     Roo.fly(body).addClass("x-tabs-body");
11326     container.appendChild(body);
11327     return body;
11328 };
11329 /** @private */
11330 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11331     var body = Roo.getDom(id);
11332     if(!body){
11333         body = document.createElement("div");
11334         body.id = id;
11335     }
11336     Roo.fly(body).addClass("x-tabs-item-body");
11337     bodyEl.insertBefore(body, bodyEl.firstChild);
11338     return body;
11339 };
11340 /** @private */
11341 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11342     var td = document.createElement("td");
11343     stripEl.appendChild(td);
11344     if(closable){
11345         td.className = "x-tabs-closable";
11346         if(!this.closeTpl){
11347             this.closeTpl = new Roo.Template(
11348                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11349                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11350                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11351             );
11352         }
11353         var el = this.closeTpl.overwrite(td, {"text": text});
11354         var close = el.getElementsByTagName("div")[0];
11355         var inner = el.getElementsByTagName("em")[0];
11356         return {"el": el, "close": close, "inner": inner};
11357     } else {
11358         if(!this.tabTpl){
11359             this.tabTpl = new Roo.Template(
11360                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11361                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11362             );
11363         }
11364         var el = this.tabTpl.overwrite(td, {"text": text});
11365         var inner = el.getElementsByTagName("em")[0];
11366         return {"el": el, "inner": inner};
11367     }
11368 };/*
11369  * Based on:
11370  * Ext JS Library 1.1.1
11371  * Copyright(c) 2006-2007, Ext JS, LLC.
11372  *
11373  * Originally Released Under LGPL - original licence link has changed is not relivant.
11374  *
11375  * Fork - LGPL
11376  * <script type="text/javascript">
11377  */
11378
11379 /**
11380  * @class Roo.Button
11381  * @extends Roo.util.Observable
11382  * Simple Button class
11383  * @cfg {String} text The button text
11384  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11385  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11386  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11387  * @cfg {Object} scope The scope of the handler
11388  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11389  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11390  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11391  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11392  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11393  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11394    applies if enableToggle = true)
11395  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11396  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11397   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11398  * @constructor
11399  * Create a new button
11400  * @param {Object} config The config object
11401  */
11402 Roo.Button = function(renderTo, config)
11403 {
11404     if (!config) {
11405         config = renderTo;
11406         renderTo = config.renderTo || false;
11407     }
11408     
11409     Roo.apply(this, config);
11410     this.addEvents({
11411         /**
11412              * @event click
11413              * Fires when this button is clicked
11414              * @param {Button} this
11415              * @param {EventObject} e The click event
11416              */
11417             "click" : true,
11418         /**
11419              * @event toggle
11420              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11421              * @param {Button} this
11422              * @param {Boolean} pressed
11423              */
11424             "toggle" : true,
11425         /**
11426              * @event mouseover
11427              * Fires when the mouse hovers over the button
11428              * @param {Button} this
11429              * @param {Event} e The event object
11430              */
11431         'mouseover' : true,
11432         /**
11433              * @event mouseout
11434              * Fires when the mouse exits the button
11435              * @param {Button} this
11436              * @param {Event} e The event object
11437              */
11438         'mouseout': true,
11439          /**
11440              * @event render
11441              * Fires when the button is rendered
11442              * @param {Button} this
11443              */
11444         'render': true
11445     });
11446     if(this.menu){
11447         this.menu = Roo.menu.MenuMgr.get(this.menu);
11448     }
11449     // register listeners first!!  - so render can be captured..
11450     Roo.util.Observable.call(this);
11451     if(renderTo){
11452         this.render(renderTo);
11453     }
11454     
11455   
11456 };
11457
11458 Roo.extend(Roo.Button, Roo.util.Observable, {
11459     /**
11460      * 
11461      */
11462     
11463     /**
11464      * Read-only. True if this button is hidden
11465      * @type Boolean
11466      */
11467     hidden : false,
11468     /**
11469      * Read-only. True if this button is disabled
11470      * @type Boolean
11471      */
11472     disabled : false,
11473     /**
11474      * Read-only. True if this button is pressed (only if enableToggle = true)
11475      * @type Boolean
11476      */
11477     pressed : false,
11478
11479     /**
11480      * @cfg {Number} tabIndex 
11481      * The DOM tabIndex for this button (defaults to undefined)
11482      */
11483     tabIndex : undefined,
11484
11485     /**
11486      * @cfg {Boolean} enableToggle
11487      * True to enable pressed/not pressed toggling (defaults to false)
11488      */
11489     enableToggle: false,
11490     /**
11491      * @cfg {Mixed} menu
11492      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11493      */
11494     menu : undefined,
11495     /**
11496      * @cfg {String} menuAlign
11497      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11498      */
11499     menuAlign : "tl-bl?",
11500
11501     /**
11502      * @cfg {String} iconCls
11503      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11504      */
11505     iconCls : undefined,
11506     /**
11507      * @cfg {String} type
11508      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11509      */
11510     type : 'button',
11511
11512     // private
11513     menuClassTarget: 'tr',
11514
11515     /**
11516      * @cfg {String} clickEvent
11517      * The type of event to map to the button's event handler (defaults to 'click')
11518      */
11519     clickEvent : 'click',
11520
11521     /**
11522      * @cfg {Boolean} handleMouseEvents
11523      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11524      */
11525     handleMouseEvents : true,
11526
11527     /**
11528      * @cfg {String} tooltipType
11529      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11530      */
11531     tooltipType : 'qtip',
11532
11533     /**
11534      * @cfg {String} cls
11535      * A CSS class to apply to the button's main element.
11536      */
11537     
11538     /**
11539      * @cfg {Roo.Template} template (Optional)
11540      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11541      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11542      * require code modifications if required elements (e.g. a button) aren't present.
11543      */
11544
11545     // private
11546     render : function(renderTo){
11547         var btn;
11548         if(this.hideParent){
11549             this.parentEl = Roo.get(renderTo);
11550         }
11551         if(!this.dhconfig){
11552             if(!this.template){
11553                 if(!Roo.Button.buttonTemplate){
11554                     // hideous table template
11555                     Roo.Button.buttonTemplate = new Roo.Template(
11556                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11557                         '<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>',
11558                         "</tr></tbody></table>");
11559                 }
11560                 this.template = Roo.Button.buttonTemplate;
11561             }
11562             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11563             var btnEl = btn.child("button:first");
11564             btnEl.on('focus', this.onFocus, this);
11565             btnEl.on('blur', this.onBlur, this);
11566             if(this.cls){
11567                 btn.addClass(this.cls);
11568             }
11569             if(this.icon){
11570                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11571             }
11572             if(this.iconCls){
11573                 btnEl.addClass(this.iconCls);
11574                 if(!this.cls){
11575                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11576                 }
11577             }
11578             if(this.tabIndex !== undefined){
11579                 btnEl.dom.tabIndex = this.tabIndex;
11580             }
11581             if(this.tooltip){
11582                 if(typeof this.tooltip == 'object'){
11583                     Roo.QuickTips.tips(Roo.apply({
11584                           target: btnEl.id
11585                     }, this.tooltip));
11586                 } else {
11587                     btnEl.dom[this.tooltipType] = this.tooltip;
11588                 }
11589             }
11590         }else{
11591             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11592         }
11593         this.el = btn;
11594         if(this.id){
11595             this.el.dom.id = this.el.id = this.id;
11596         }
11597         if(this.menu){
11598             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11599             this.menu.on("show", this.onMenuShow, this);
11600             this.menu.on("hide", this.onMenuHide, this);
11601         }
11602         btn.addClass("x-btn");
11603         if(Roo.isIE && !Roo.isIE7){
11604             this.autoWidth.defer(1, this);
11605         }else{
11606             this.autoWidth();
11607         }
11608         if(this.handleMouseEvents){
11609             btn.on("mouseover", this.onMouseOver, this);
11610             btn.on("mouseout", this.onMouseOut, this);
11611             btn.on("mousedown", this.onMouseDown, this);
11612         }
11613         btn.on(this.clickEvent, this.onClick, this);
11614         //btn.on("mouseup", this.onMouseUp, this);
11615         if(this.hidden){
11616             this.hide();
11617         }
11618         if(this.disabled){
11619             this.disable();
11620         }
11621         Roo.ButtonToggleMgr.register(this);
11622         if(this.pressed){
11623             this.el.addClass("x-btn-pressed");
11624         }
11625         if(this.repeat){
11626             var repeater = new Roo.util.ClickRepeater(btn,
11627                 typeof this.repeat == "object" ? this.repeat : {}
11628             );
11629             repeater.on("click", this.onClick,  this);
11630         }
11631         
11632         this.fireEvent('render', this);
11633         
11634     },
11635     /**
11636      * Returns the button's underlying element
11637      * @return {Roo.Element} The element
11638      */
11639     getEl : function(){
11640         return this.el;  
11641     },
11642     
11643     /**
11644      * Destroys this Button and removes any listeners.
11645      */
11646     destroy : function(){
11647         Roo.ButtonToggleMgr.unregister(this);
11648         this.el.removeAllListeners();
11649         this.purgeListeners();
11650         this.el.remove();
11651     },
11652
11653     // private
11654     autoWidth : function(){
11655         if(this.el){
11656             this.el.setWidth("auto");
11657             if(Roo.isIE7 && Roo.isStrict){
11658                 var ib = this.el.child('button');
11659                 if(ib && ib.getWidth() > 20){
11660                     ib.clip();
11661                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11662                 }
11663             }
11664             if(this.minWidth){
11665                 if(this.hidden){
11666                     this.el.beginMeasure();
11667                 }
11668                 if(this.el.getWidth() < this.minWidth){
11669                     this.el.setWidth(this.minWidth);
11670                 }
11671                 if(this.hidden){
11672                     this.el.endMeasure();
11673                 }
11674             }
11675         }
11676     },
11677
11678     /**
11679      * Assigns this button's click handler
11680      * @param {Function} handler The function to call when the button is clicked
11681      * @param {Object} scope (optional) Scope for the function passed in
11682      */
11683     setHandler : function(handler, scope){
11684         this.handler = handler;
11685         this.scope = scope;  
11686     },
11687     
11688     /**
11689      * Sets this button's text
11690      * @param {String} text The button text
11691      */
11692     setText : function(text){
11693         this.text = text;
11694         if(this.el){
11695             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11696         }
11697         this.autoWidth();
11698     },
11699     
11700     /**
11701      * Gets the text for this button
11702      * @return {String} The button text
11703      */
11704     getText : function(){
11705         return this.text;  
11706     },
11707     
11708     /**
11709      * Show this button
11710      */
11711     show: function(){
11712         this.hidden = false;
11713         if(this.el){
11714             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11715         }
11716     },
11717     
11718     /**
11719      * Hide this button
11720      */
11721     hide: function(){
11722         this.hidden = true;
11723         if(this.el){
11724             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11725         }
11726     },
11727     
11728     /**
11729      * Convenience function for boolean show/hide
11730      * @param {Boolean} visible True to show, false to hide
11731      */
11732     setVisible: function(visible){
11733         if(visible) {
11734             this.show();
11735         }else{
11736             this.hide();
11737         }
11738     },
11739     
11740     /**
11741      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11742      * @param {Boolean} state (optional) Force a particular state
11743      */
11744     toggle : function(state){
11745         state = state === undefined ? !this.pressed : state;
11746         if(state != this.pressed){
11747             if(state){
11748                 this.el.addClass("x-btn-pressed");
11749                 this.pressed = true;
11750                 this.fireEvent("toggle", this, true);
11751             }else{
11752                 this.el.removeClass("x-btn-pressed");
11753                 this.pressed = false;
11754                 this.fireEvent("toggle", this, false);
11755             }
11756             if(this.toggleHandler){
11757                 this.toggleHandler.call(this.scope || this, this, state);
11758             }
11759         }
11760     },
11761     
11762     /**
11763      * Focus the button
11764      */
11765     focus : function(){
11766         this.el.child('button:first').focus();
11767     },
11768     
11769     /**
11770      * Disable this button
11771      */
11772     disable : function(){
11773         if(this.el){
11774             this.el.addClass("x-btn-disabled");
11775         }
11776         this.disabled = true;
11777     },
11778     
11779     /**
11780      * Enable this button
11781      */
11782     enable : function(){
11783         if(this.el){
11784             this.el.removeClass("x-btn-disabled");
11785         }
11786         this.disabled = false;
11787     },
11788
11789     /**
11790      * Convenience function for boolean enable/disable
11791      * @param {Boolean} enabled True to enable, false to disable
11792      */
11793     setDisabled : function(v){
11794         this[v !== true ? "enable" : "disable"]();
11795     },
11796
11797     // private
11798     onClick : function(e){
11799         if(e){
11800             e.preventDefault();
11801         }
11802         if(e.button != 0){
11803             return;
11804         }
11805         if(!this.disabled){
11806             if(this.enableToggle){
11807                 this.toggle();
11808             }
11809             if(this.menu && !this.menu.isVisible()){
11810                 this.menu.show(this.el, this.menuAlign);
11811             }
11812             this.fireEvent("click", this, e);
11813             if(this.handler){
11814                 this.el.removeClass("x-btn-over");
11815                 this.handler.call(this.scope || this, this, e);
11816             }
11817         }
11818     },
11819     // private
11820     onMouseOver : function(e){
11821         if(!this.disabled){
11822             this.el.addClass("x-btn-over");
11823             this.fireEvent('mouseover', this, e);
11824         }
11825     },
11826     // private
11827     onMouseOut : function(e){
11828         if(!e.within(this.el,  true)){
11829             this.el.removeClass("x-btn-over");
11830             this.fireEvent('mouseout', this, e);
11831         }
11832     },
11833     // private
11834     onFocus : function(e){
11835         if(!this.disabled){
11836             this.el.addClass("x-btn-focus");
11837         }
11838     },
11839     // private
11840     onBlur : function(e){
11841         this.el.removeClass("x-btn-focus");
11842     },
11843     // private
11844     onMouseDown : function(e){
11845         if(!this.disabled && e.button == 0){
11846             this.el.addClass("x-btn-click");
11847             Roo.get(document).on('mouseup', this.onMouseUp, this);
11848         }
11849     },
11850     // private
11851     onMouseUp : function(e){
11852         if(e.button == 0){
11853             this.el.removeClass("x-btn-click");
11854             Roo.get(document).un('mouseup', this.onMouseUp, this);
11855         }
11856     },
11857     // private
11858     onMenuShow : function(e){
11859         this.el.addClass("x-btn-menu-active");
11860     },
11861     // private
11862     onMenuHide : function(e){
11863         this.el.removeClass("x-btn-menu-active");
11864     }   
11865 });
11866
11867 // Private utility class used by Button
11868 Roo.ButtonToggleMgr = function(){
11869    var groups = {};
11870    
11871    function toggleGroup(btn, state){
11872        if(state){
11873            var g = groups[btn.toggleGroup];
11874            for(var i = 0, l = g.length; i < l; i++){
11875                if(g[i] != btn){
11876                    g[i].toggle(false);
11877                }
11878            }
11879        }
11880    }
11881    
11882    return {
11883        register : function(btn){
11884            if(!btn.toggleGroup){
11885                return;
11886            }
11887            var g = groups[btn.toggleGroup];
11888            if(!g){
11889                g = groups[btn.toggleGroup] = [];
11890            }
11891            g.push(btn);
11892            btn.on("toggle", toggleGroup);
11893        },
11894        
11895        unregister : function(btn){
11896            if(!btn.toggleGroup){
11897                return;
11898            }
11899            var g = groups[btn.toggleGroup];
11900            if(g){
11901                g.remove(btn);
11902                btn.un("toggle", toggleGroup);
11903            }
11904        }
11905    };
11906 }();/*
11907  * Based on:
11908  * Ext JS Library 1.1.1
11909  * Copyright(c) 2006-2007, Ext JS, LLC.
11910  *
11911  * Originally Released Under LGPL - original licence link has changed is not relivant.
11912  *
11913  * Fork - LGPL
11914  * <script type="text/javascript">
11915  */
11916  
11917 /**
11918  * @class Roo.SplitButton
11919  * @extends Roo.Button
11920  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11921  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11922  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11923  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11924  * @cfg {String} arrowTooltip The title attribute of the arrow
11925  * @constructor
11926  * Create a new menu button
11927  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11928  * @param {Object} config The config object
11929  */
11930 Roo.SplitButton = function(renderTo, config){
11931     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11932     /**
11933      * @event arrowclick
11934      * Fires when this button's arrow is clicked
11935      * @param {SplitButton} this
11936      * @param {EventObject} e The click event
11937      */
11938     this.addEvents({"arrowclick":true});
11939 };
11940
11941 Roo.extend(Roo.SplitButton, Roo.Button, {
11942     render : function(renderTo){
11943         // this is one sweet looking template!
11944         var tpl = new Roo.Template(
11945             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11946             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11947             '<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>',
11948             "</tbody></table></td><td>",
11949             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11950             '<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>',
11951             "</tbody></table></td></tr></table>"
11952         );
11953         var btn = tpl.append(renderTo, [this.text, this.type], true);
11954         var btnEl = btn.child("button");
11955         if(this.cls){
11956             btn.addClass(this.cls);
11957         }
11958         if(this.icon){
11959             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11960         }
11961         if(this.iconCls){
11962             btnEl.addClass(this.iconCls);
11963             if(!this.cls){
11964                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11965             }
11966         }
11967         this.el = btn;
11968         if(this.handleMouseEvents){
11969             btn.on("mouseover", this.onMouseOver, this);
11970             btn.on("mouseout", this.onMouseOut, this);
11971             btn.on("mousedown", this.onMouseDown, this);
11972             btn.on("mouseup", this.onMouseUp, this);
11973         }
11974         btn.on(this.clickEvent, this.onClick, this);
11975         if(this.tooltip){
11976             if(typeof this.tooltip == 'object'){
11977                 Roo.QuickTips.tips(Roo.apply({
11978                       target: btnEl.id
11979                 }, this.tooltip));
11980             } else {
11981                 btnEl.dom[this.tooltipType] = this.tooltip;
11982             }
11983         }
11984         if(this.arrowTooltip){
11985             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11986         }
11987         if(this.hidden){
11988             this.hide();
11989         }
11990         if(this.disabled){
11991             this.disable();
11992         }
11993         if(this.pressed){
11994             this.el.addClass("x-btn-pressed");
11995         }
11996         if(Roo.isIE && !Roo.isIE7){
11997             this.autoWidth.defer(1, this);
11998         }else{
11999             this.autoWidth();
12000         }
12001         if(this.menu){
12002             this.menu.on("show", this.onMenuShow, this);
12003             this.menu.on("hide", this.onMenuHide, this);
12004         }
12005         this.fireEvent('render', this);
12006     },
12007
12008     // private
12009     autoWidth : function(){
12010         if(this.el){
12011             var tbl = this.el.child("table:first");
12012             var tbl2 = this.el.child("table:last");
12013             this.el.setWidth("auto");
12014             tbl.setWidth("auto");
12015             if(Roo.isIE7 && Roo.isStrict){
12016                 var ib = this.el.child('button:first');
12017                 if(ib && ib.getWidth() > 20){
12018                     ib.clip();
12019                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12020                 }
12021             }
12022             if(this.minWidth){
12023                 if(this.hidden){
12024                     this.el.beginMeasure();
12025                 }
12026                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12027                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12028                 }
12029                 if(this.hidden){
12030                     this.el.endMeasure();
12031                 }
12032             }
12033             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12034         } 
12035     },
12036     /**
12037      * Sets this button's click handler
12038      * @param {Function} handler The function to call when the button is clicked
12039      * @param {Object} scope (optional) Scope for the function passed above
12040      */
12041     setHandler : function(handler, scope){
12042         this.handler = handler;
12043         this.scope = scope;  
12044     },
12045     
12046     /**
12047      * Sets this button's arrow click handler
12048      * @param {Function} handler The function to call when the arrow is clicked
12049      * @param {Object} scope (optional) Scope for the function passed above
12050      */
12051     setArrowHandler : function(handler, scope){
12052         this.arrowHandler = handler;
12053         this.scope = scope;  
12054     },
12055     
12056     /**
12057      * Focus the button
12058      */
12059     focus : function(){
12060         if(this.el){
12061             this.el.child("button:first").focus();
12062         }
12063     },
12064
12065     // private
12066     onClick : function(e){
12067         e.preventDefault();
12068         if(!this.disabled){
12069             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12070                 if(this.menu && !this.menu.isVisible()){
12071                     this.menu.show(this.el, this.menuAlign);
12072                 }
12073                 this.fireEvent("arrowclick", this, e);
12074                 if(this.arrowHandler){
12075                     this.arrowHandler.call(this.scope || this, this, e);
12076                 }
12077             }else{
12078                 this.fireEvent("click", this, e);
12079                 if(this.handler){
12080                     this.handler.call(this.scope || this, this, e);
12081                 }
12082             }
12083         }
12084     },
12085     // private
12086     onMouseDown : function(e){
12087         if(!this.disabled){
12088             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12089         }
12090     },
12091     // private
12092     onMouseUp : function(e){
12093         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12094     }   
12095 });
12096
12097
12098 // backwards compat
12099 Roo.MenuButton = Roo.SplitButton;/*
12100  * Based on:
12101  * Ext JS Library 1.1.1
12102  * Copyright(c) 2006-2007, Ext JS, LLC.
12103  *
12104  * Originally Released Under LGPL - original licence link has changed is not relivant.
12105  *
12106  * Fork - LGPL
12107  * <script type="text/javascript">
12108  */
12109
12110 /**
12111  * @class Roo.Toolbar
12112  * Basic Toolbar class.
12113  * @constructor
12114  * Creates a new Toolbar
12115  * @param {Object} config The config object
12116  */ 
12117 Roo.Toolbar = function(container, buttons, config)
12118 {
12119     /// old consturctor format still supported..
12120     if(container instanceof Array){ // omit the container for later rendering
12121         buttons = container;
12122         config = buttons;
12123         container = null;
12124     }
12125     if (typeof(container) == 'object' && container.xtype) {
12126         config = container;
12127         container = config.container;
12128         buttons = config.buttons; // not really - use items!!
12129     }
12130     var xitems = [];
12131     if (config && config.items) {
12132         xitems = config.items;
12133         delete config.items;
12134     }
12135     Roo.apply(this, config);
12136     this.buttons = buttons;
12137     
12138     if(container){
12139         this.render(container);
12140     }
12141     Roo.each(xitems, function(b) {
12142         this.add(b);
12143     }, this);
12144     
12145 };
12146
12147 Roo.Toolbar.prototype = {
12148     /**
12149      * @cfg {Roo.data.Store} items
12150      * array of button configs or elements to add
12151      */
12152     
12153     /**
12154      * @cfg {String/HTMLElement/Element} container
12155      * The id or element that will contain the toolbar
12156      */
12157     // private
12158     render : function(ct){
12159         this.el = Roo.get(ct);
12160         if(this.cls){
12161             this.el.addClass(this.cls);
12162         }
12163         // using a table allows for vertical alignment
12164         // 100% width is needed by Safari...
12165         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12166         this.tr = this.el.child("tr", true);
12167         var autoId = 0;
12168         this.items = new Roo.util.MixedCollection(false, function(o){
12169             return o.id || ("item" + (++autoId));
12170         });
12171         if(this.buttons){
12172             this.add.apply(this, this.buttons);
12173             delete this.buttons;
12174         }
12175     },
12176
12177     /**
12178      * Adds element(s) to the toolbar -- this function takes a variable number of 
12179      * arguments of mixed type and adds them to the toolbar.
12180      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12181      * <ul>
12182      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12183      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12184      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12185      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12186      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12187      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12188      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12189      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12190      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12191      * </ul>
12192      * @param {Mixed} arg2
12193      * @param {Mixed} etc.
12194      */
12195     add : function(){
12196         var a = arguments, l = a.length;
12197         for(var i = 0; i < l; i++){
12198             this._add(a[i]);
12199         }
12200     },
12201     // private..
12202     _add : function(el) {
12203         
12204         if (el.xtype) {
12205             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12206         }
12207         
12208         if (el.applyTo){ // some kind of form field
12209             return this.addField(el);
12210         } 
12211         if (el.render){ // some kind of Toolbar.Item
12212             return this.addItem(el);
12213         }
12214         if (typeof el == "string"){ // string
12215             if(el == "separator" || el == "-"){
12216                 return this.addSeparator();
12217             }
12218             if (el == " "){
12219                 return this.addSpacer();
12220             }
12221             if(el == "->"){
12222                 return this.addFill();
12223             }
12224             return this.addText(el);
12225             
12226         }
12227         if(el.tagName){ // element
12228             return this.addElement(el);
12229         }
12230         if(typeof el == "object"){ // must be button config?
12231             return this.addButton(el);
12232         }
12233         // and now what?!?!
12234         return false;
12235         
12236     },
12237     
12238     /**
12239      * Add an Xtype element
12240      * @param {Object} xtype Xtype Object
12241      * @return {Object} created Object
12242      */
12243     addxtype : function(e){
12244         return this.add(e);  
12245     },
12246     
12247     /**
12248      * Returns the Element for this toolbar.
12249      * @return {Roo.Element}
12250      */
12251     getEl : function(){
12252         return this.el;  
12253     },
12254     
12255     /**
12256      * Adds a separator
12257      * @return {Roo.Toolbar.Item} The separator item
12258      */
12259     addSeparator : function(){
12260         return this.addItem(new Roo.Toolbar.Separator());
12261     },
12262
12263     /**
12264      * Adds a spacer element
12265      * @return {Roo.Toolbar.Spacer} The spacer item
12266      */
12267     addSpacer : function(){
12268         return this.addItem(new Roo.Toolbar.Spacer());
12269     },
12270
12271     /**
12272      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12273      * @return {Roo.Toolbar.Fill} The fill item
12274      */
12275     addFill : function(){
12276         return this.addItem(new Roo.Toolbar.Fill());
12277     },
12278
12279     /**
12280      * Adds any standard HTML element to the toolbar
12281      * @param {String/HTMLElement/Element} el The element or id of the element to add
12282      * @return {Roo.Toolbar.Item} The element's item
12283      */
12284     addElement : function(el){
12285         return this.addItem(new Roo.Toolbar.Item(el));
12286     },
12287     /**
12288      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12289      * @type Roo.util.MixedCollection  
12290      */
12291     items : false,
12292      
12293     /**
12294      * Adds any Toolbar.Item or subclass
12295      * @param {Roo.Toolbar.Item} item
12296      * @return {Roo.Toolbar.Item} The item
12297      */
12298     addItem : function(item){
12299         var td = this.nextBlock();
12300         item.render(td);
12301         this.items.add(item);
12302         return item;
12303     },
12304     
12305     /**
12306      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12307      * @param {Object/Array} config A button config or array of configs
12308      * @return {Roo.Toolbar.Button/Array}
12309      */
12310     addButton : function(config){
12311         if(config instanceof Array){
12312             var buttons = [];
12313             for(var i = 0, len = config.length; i < len; i++) {
12314                 buttons.push(this.addButton(config[i]));
12315             }
12316             return buttons;
12317         }
12318         var b = config;
12319         if(!(config instanceof Roo.Toolbar.Button)){
12320             b = config.split ?
12321                 new Roo.Toolbar.SplitButton(config) :
12322                 new Roo.Toolbar.Button(config);
12323         }
12324         var td = this.nextBlock();
12325         b.render(td);
12326         this.items.add(b);
12327         return b;
12328     },
12329     
12330     /**
12331      * Adds text to the toolbar
12332      * @param {String} text The text to add
12333      * @return {Roo.Toolbar.Item} The element's item
12334      */
12335     addText : function(text){
12336         return this.addItem(new Roo.Toolbar.TextItem(text));
12337     },
12338     
12339     /**
12340      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12341      * @param {Number} index The index where the item is to be inserted
12342      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12343      * @return {Roo.Toolbar.Button/Item}
12344      */
12345     insertButton : function(index, item){
12346         if(item instanceof Array){
12347             var buttons = [];
12348             for(var i = 0, len = item.length; i < len; i++) {
12349                buttons.push(this.insertButton(index + i, item[i]));
12350             }
12351             return buttons;
12352         }
12353         if (!(item instanceof Roo.Toolbar.Button)){
12354            item = new Roo.Toolbar.Button(item);
12355         }
12356         var td = document.createElement("td");
12357         this.tr.insertBefore(td, this.tr.childNodes[index]);
12358         item.render(td);
12359         this.items.insert(index, item);
12360         return item;
12361     },
12362     
12363     /**
12364      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12365      * @param {Object} config
12366      * @return {Roo.Toolbar.Item} The element's item
12367      */
12368     addDom : function(config, returnEl){
12369         var td = this.nextBlock();
12370         Roo.DomHelper.overwrite(td, config);
12371         var ti = new Roo.Toolbar.Item(td.firstChild);
12372         ti.render(td);
12373         this.items.add(ti);
12374         return ti;
12375     },
12376
12377     /**
12378      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12379      * @type Roo.util.MixedCollection  
12380      */
12381     fields : false,
12382     
12383     /**
12384      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12385      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12386      * @param {Roo.form.Field} field
12387      * @return {Roo.ToolbarItem}
12388      */
12389      
12390       
12391     addField : function(field) {
12392         if (!this.fields) {
12393             var autoId = 0;
12394             this.fields = new Roo.util.MixedCollection(false, function(o){
12395                 return o.id || ("item" + (++autoId));
12396             });
12397
12398         }
12399         
12400         var td = this.nextBlock();
12401         field.render(td);
12402         var ti = new Roo.Toolbar.Item(td.firstChild);
12403         ti.render(td);
12404         this.items.add(ti);
12405         this.fields.add(field);
12406         return ti;
12407     },
12408     /**
12409      * Hide the toolbar
12410      * @method hide
12411      */
12412      
12413       
12414     hide : function()
12415     {
12416         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12417         this.el.child('div').hide();
12418     },
12419     /**
12420      * Show the toolbar
12421      * @method show
12422      */
12423     show : function()
12424     {
12425         this.el.child('div').show();
12426     },
12427       
12428     // private
12429     nextBlock : function(){
12430         var td = document.createElement("td");
12431         this.tr.appendChild(td);
12432         return td;
12433     },
12434
12435     // private
12436     destroy : function(){
12437         if(this.items){ // rendered?
12438             Roo.destroy.apply(Roo, this.items.items);
12439         }
12440         if(this.fields){ // rendered?
12441             Roo.destroy.apply(Roo, this.fields.items);
12442         }
12443         Roo.Element.uncache(this.el, this.tr);
12444     }
12445 };
12446
12447 /**
12448  * @class Roo.Toolbar.Item
12449  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12450  * @constructor
12451  * Creates a new Item
12452  * @param {HTMLElement} el 
12453  */
12454 Roo.Toolbar.Item = function(el){
12455     this.el = Roo.getDom(el);
12456     this.id = Roo.id(this.el);
12457     this.hidden = false;
12458 };
12459
12460 Roo.Toolbar.Item.prototype = {
12461     
12462     /**
12463      * Get this item's HTML Element
12464      * @return {HTMLElement}
12465      */
12466     getEl : function(){
12467        return this.el;  
12468     },
12469
12470     // private
12471     render : function(td){
12472         this.td = td;
12473         td.appendChild(this.el);
12474     },
12475     
12476     /**
12477      * Removes and destroys this item.
12478      */
12479     destroy : function(){
12480         this.td.parentNode.removeChild(this.td);
12481     },
12482     
12483     /**
12484      * Shows this item.
12485      */
12486     show: function(){
12487         this.hidden = false;
12488         this.td.style.display = "";
12489     },
12490     
12491     /**
12492      * Hides this item.
12493      */
12494     hide: function(){
12495         this.hidden = true;
12496         this.td.style.display = "none";
12497     },
12498     
12499     /**
12500      * Convenience function for boolean show/hide.
12501      * @param {Boolean} visible true to show/false to hide
12502      */
12503     setVisible: function(visible){
12504         if(visible) {
12505             this.show();
12506         }else{
12507             this.hide();
12508         }
12509     },
12510     
12511     /**
12512      * Try to focus this item.
12513      */
12514     focus : function(){
12515         Roo.fly(this.el).focus();
12516     },
12517     
12518     /**
12519      * Disables this item.
12520      */
12521     disable : function(){
12522         Roo.fly(this.td).addClass("x-item-disabled");
12523         this.disabled = true;
12524         this.el.disabled = true;
12525     },
12526     
12527     /**
12528      * Enables this item.
12529      */
12530     enable : function(){
12531         Roo.fly(this.td).removeClass("x-item-disabled");
12532         this.disabled = false;
12533         this.el.disabled = false;
12534     }
12535 };
12536
12537
12538 /**
12539  * @class Roo.Toolbar.Separator
12540  * @extends Roo.Toolbar.Item
12541  * A simple toolbar separator class
12542  * @constructor
12543  * Creates a new Separator
12544  */
12545 Roo.Toolbar.Separator = function(){
12546     var s = document.createElement("span");
12547     s.className = "ytb-sep";
12548     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12549 };
12550 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12551     enable:Roo.emptyFn,
12552     disable:Roo.emptyFn,
12553     focus:Roo.emptyFn
12554 });
12555
12556 /**
12557  * @class Roo.Toolbar.Spacer
12558  * @extends Roo.Toolbar.Item
12559  * A simple element that adds extra horizontal space to a toolbar.
12560  * @constructor
12561  * Creates a new Spacer
12562  */
12563 Roo.Toolbar.Spacer = function(){
12564     var s = document.createElement("div");
12565     s.className = "ytb-spacer";
12566     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12567 };
12568 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12569     enable:Roo.emptyFn,
12570     disable:Roo.emptyFn,
12571     focus:Roo.emptyFn
12572 });
12573
12574 /**
12575  * @class Roo.Toolbar.Fill
12576  * @extends Roo.Toolbar.Spacer
12577  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12578  * @constructor
12579  * Creates a new Spacer
12580  */
12581 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12582     // private
12583     render : function(td){
12584         td.style.width = '100%';
12585         Roo.Toolbar.Fill.superclass.render.call(this, td);
12586     }
12587 });
12588
12589 /**
12590  * @class Roo.Toolbar.TextItem
12591  * @extends Roo.Toolbar.Item
12592  * A simple class that renders text directly into a toolbar.
12593  * @constructor
12594  * Creates a new TextItem
12595  * @param {String} text
12596  */
12597 Roo.Toolbar.TextItem = function(text){
12598     if (typeof(text) == 'object') {
12599         text = text.text;
12600     }
12601     var s = document.createElement("span");
12602     s.className = "ytb-text";
12603     s.innerHTML = text;
12604     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12605 };
12606 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12607     enable:Roo.emptyFn,
12608     disable:Roo.emptyFn,
12609     focus:Roo.emptyFn
12610 });
12611
12612 /**
12613  * @class Roo.Toolbar.Button
12614  * @extends Roo.Button
12615  * A button that renders into a toolbar.
12616  * @constructor
12617  * Creates a new Button
12618  * @param {Object} config A standard {@link Roo.Button} config object
12619  */
12620 Roo.Toolbar.Button = function(config){
12621     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12622 };
12623 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12624     render : function(td){
12625         this.td = td;
12626         Roo.Toolbar.Button.superclass.render.call(this, td);
12627     },
12628     
12629     /**
12630      * Removes and destroys this button
12631      */
12632     destroy : function(){
12633         Roo.Toolbar.Button.superclass.destroy.call(this);
12634         this.td.parentNode.removeChild(this.td);
12635     },
12636     
12637     /**
12638      * Shows this button
12639      */
12640     show: function(){
12641         this.hidden = false;
12642         this.td.style.display = "";
12643     },
12644     
12645     /**
12646      * Hides this button
12647      */
12648     hide: function(){
12649         this.hidden = true;
12650         this.td.style.display = "none";
12651     },
12652
12653     /**
12654      * Disables this item
12655      */
12656     disable : function(){
12657         Roo.fly(this.td).addClass("x-item-disabled");
12658         this.disabled = true;
12659     },
12660
12661     /**
12662      * Enables this item
12663      */
12664     enable : function(){
12665         Roo.fly(this.td).removeClass("x-item-disabled");
12666         this.disabled = false;
12667     }
12668 });
12669 // backwards compat
12670 Roo.ToolbarButton = Roo.Toolbar.Button;
12671
12672 /**
12673  * @class Roo.Toolbar.SplitButton
12674  * @extends Roo.SplitButton
12675  * A menu button that renders into a toolbar.
12676  * @constructor
12677  * Creates a new SplitButton
12678  * @param {Object} config A standard {@link Roo.SplitButton} config object
12679  */
12680 Roo.Toolbar.SplitButton = function(config){
12681     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12682 };
12683 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12684     render : function(td){
12685         this.td = td;
12686         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12687     },
12688     
12689     /**
12690      * Removes and destroys this button
12691      */
12692     destroy : function(){
12693         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12694         this.td.parentNode.removeChild(this.td);
12695     },
12696     
12697     /**
12698      * Shows this button
12699      */
12700     show: function(){
12701         this.hidden = false;
12702         this.td.style.display = "";
12703     },
12704     
12705     /**
12706      * Hides this button
12707      */
12708     hide: function(){
12709         this.hidden = true;
12710         this.td.style.display = "none";
12711     }
12712 });
12713
12714 // backwards compat
12715 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12716  * Based on:
12717  * Ext JS Library 1.1.1
12718  * Copyright(c) 2006-2007, Ext JS, LLC.
12719  *
12720  * Originally Released Under LGPL - original licence link has changed is not relivant.
12721  *
12722  * Fork - LGPL
12723  * <script type="text/javascript">
12724  */
12725  
12726 /**
12727  * @class Roo.PagingToolbar
12728  * @extends Roo.Toolbar
12729  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12730  * @constructor
12731  * Create a new PagingToolbar
12732  * @param {Object} config The config object
12733  */
12734 Roo.PagingToolbar = function(el, ds, config)
12735 {
12736     // old args format still supported... - xtype is prefered..
12737     if (typeof(el) == 'object' && el.xtype) {
12738         // created from xtype...
12739         config = el;
12740         ds = el.dataSource;
12741         el = config.container;
12742     }
12743     var items = [];
12744     if (config.items) {
12745         items = this.items;
12746         config.items = [];
12747     }
12748     
12749     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12750     this.ds = ds;
12751     this.cursor = 0;
12752     this.renderButtons(this.el);
12753     this.bind(ds);
12754     
12755     // supprot items array.
12756     var _this = this;
12757     Roo.each(items, function(e) {
12758         _this.add(Roo.factory(e));
12759     });
12760     
12761 };
12762
12763 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12764     /**
12765      * @cfg {Roo.data.Store} dataSource
12766      * The underlying data store providing the paged data
12767      */
12768     /**
12769      * @cfg {String/HTMLElement/Element} container
12770      * container The id or element that will contain the toolbar
12771      */
12772     /**
12773      * @cfg {Boolean} displayInfo
12774      * True to display the displayMsg (defaults to false)
12775      */
12776     /**
12777      * @cfg {Number} pageSize
12778      * The number of records to display per page (defaults to 20)
12779      */
12780     pageSize: 20,
12781     /**
12782      * @cfg {String} displayMsg
12783      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12784      */
12785     displayMsg : 'Displaying {0} - {1} of {2}',
12786     /**
12787      * @cfg {String} emptyMsg
12788      * The message to display when no records are found (defaults to "No data to display")
12789      */
12790     emptyMsg : 'No data to display',
12791     /**
12792      * Customizable piece of the default paging text (defaults to "Page")
12793      * @type String
12794      */
12795     beforePageText : "Page",
12796     /**
12797      * Customizable piece of the default paging text (defaults to "of %0")
12798      * @type String
12799      */
12800     afterPageText : "of {0}",
12801     /**
12802      * Customizable piece of the default paging text (defaults to "First Page")
12803      * @type String
12804      */
12805     firstText : "First Page",
12806     /**
12807      * Customizable piece of the default paging text (defaults to "Previous Page")
12808      * @type String
12809      */
12810     prevText : "Previous Page",
12811     /**
12812      * Customizable piece of the default paging text (defaults to "Next Page")
12813      * @type String
12814      */
12815     nextText : "Next Page",
12816     /**
12817      * Customizable piece of the default paging text (defaults to "Last Page")
12818      * @type String
12819      */
12820     lastText : "Last Page",
12821     /**
12822      * Customizable piece of the default paging text (defaults to "Refresh")
12823      * @type String
12824      */
12825     refreshText : "Refresh",
12826
12827     // private
12828     renderButtons : function(el){
12829         Roo.PagingToolbar.superclass.render.call(this, el);
12830         this.first = this.addButton({
12831             tooltip: this.firstText,
12832             cls: "x-btn-icon x-grid-page-first",
12833             disabled: true,
12834             handler: this.onClick.createDelegate(this, ["first"])
12835         });
12836         this.prev = this.addButton({
12837             tooltip: this.prevText,
12838             cls: "x-btn-icon x-grid-page-prev",
12839             disabled: true,
12840             handler: this.onClick.createDelegate(this, ["prev"])
12841         });
12842         //this.addSeparator();
12843         this.add(this.beforePageText);
12844         this.field = Roo.get(this.addDom({
12845            tag: "input",
12846            type: "text",
12847            size: "3",
12848            value: "1",
12849            cls: "x-grid-page-number"
12850         }).el);
12851         this.field.on("keydown", this.onPagingKeydown, this);
12852         this.field.on("focus", function(){this.dom.select();});
12853         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12854         this.field.setHeight(18);
12855         //this.addSeparator();
12856         this.next = this.addButton({
12857             tooltip: this.nextText,
12858             cls: "x-btn-icon x-grid-page-next",
12859             disabled: true,
12860             handler: this.onClick.createDelegate(this, ["next"])
12861         });
12862         this.last = this.addButton({
12863             tooltip: this.lastText,
12864             cls: "x-btn-icon x-grid-page-last",
12865             disabled: true,
12866             handler: this.onClick.createDelegate(this, ["last"])
12867         });
12868         //this.addSeparator();
12869         this.loading = this.addButton({
12870             tooltip: this.refreshText,
12871             cls: "x-btn-icon x-grid-loading",
12872             handler: this.onClick.createDelegate(this, ["refresh"])
12873         });
12874
12875         if(this.displayInfo){
12876             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12877         }
12878     },
12879
12880     // private
12881     updateInfo : function(){
12882         if(this.displayEl){
12883             var count = this.ds.getCount();
12884             var msg = count == 0 ?
12885                 this.emptyMsg :
12886                 String.format(
12887                     this.displayMsg,
12888                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12889                 );
12890             this.displayEl.update(msg);
12891         }
12892     },
12893
12894     // private
12895     onLoad : function(ds, r, o){
12896        this.cursor = o.params ? o.params.start : 0;
12897        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12898
12899        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12900        this.field.dom.value = ap;
12901        this.first.setDisabled(ap == 1);
12902        this.prev.setDisabled(ap == 1);
12903        this.next.setDisabled(ap == ps);
12904        this.last.setDisabled(ap == ps);
12905        this.loading.enable();
12906        this.updateInfo();
12907     },
12908
12909     // private
12910     getPageData : function(){
12911         var total = this.ds.getTotalCount();
12912         return {
12913             total : total,
12914             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12915             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12916         };
12917     },
12918
12919     // private
12920     onLoadError : function(){
12921         this.loading.enable();
12922     },
12923
12924     // private
12925     onPagingKeydown : function(e){
12926         var k = e.getKey();
12927         var d = this.getPageData();
12928         if(k == e.RETURN){
12929             var v = this.field.dom.value, pageNum;
12930             if(!v || isNaN(pageNum = parseInt(v, 10))){
12931                 this.field.dom.value = d.activePage;
12932                 return;
12933             }
12934             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12935             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12936             e.stopEvent();
12937         }
12938         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))
12939         {
12940           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12941           this.field.dom.value = pageNum;
12942           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12943           e.stopEvent();
12944         }
12945         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12946         {
12947           var v = this.field.dom.value, pageNum; 
12948           var increment = (e.shiftKey) ? 10 : 1;
12949           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12950             increment *= -1;
12951           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12952             this.field.dom.value = d.activePage;
12953             return;
12954           }
12955           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12956           {
12957             this.field.dom.value = parseInt(v, 10) + increment;
12958             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12959             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12960           }
12961           e.stopEvent();
12962         }
12963     },
12964
12965     // private
12966     beforeLoad : function(){
12967         if(this.loading){
12968             this.loading.disable();
12969         }
12970     },
12971
12972     // private
12973     onClick : function(which){
12974         var ds = this.ds;
12975         switch(which){
12976             case "first":
12977                 ds.load({params:{start: 0, limit: this.pageSize}});
12978             break;
12979             case "prev":
12980                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12981             break;
12982             case "next":
12983                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12984             break;
12985             case "last":
12986                 var total = ds.getTotalCount();
12987                 var extra = total % this.pageSize;
12988                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12989                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12990             break;
12991             case "refresh":
12992                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12993             break;
12994         }
12995     },
12996
12997     /**
12998      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12999      * @param {Roo.data.Store} store The data store to unbind
13000      */
13001     unbind : function(ds){
13002         ds.un("beforeload", this.beforeLoad, this);
13003         ds.un("load", this.onLoad, this);
13004         ds.un("loadexception", this.onLoadError, this);
13005         ds.un("remove", this.updateInfo, this);
13006         ds.un("add", this.updateInfo, this);
13007         this.ds = undefined;
13008     },
13009
13010     /**
13011      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13012      * @param {Roo.data.Store} store The data store to bind
13013      */
13014     bind : function(ds){
13015         ds.on("beforeload", this.beforeLoad, this);
13016         ds.on("load", this.onLoad, this);
13017         ds.on("loadexception", this.onLoadError, this);
13018         ds.on("remove", this.updateInfo, this);
13019         ds.on("add", this.updateInfo, this);
13020         this.ds = ds;
13021     }
13022 });/*
13023  * Based on:
13024  * Ext JS Library 1.1.1
13025  * Copyright(c) 2006-2007, Ext JS, LLC.
13026  *
13027  * Originally Released Under LGPL - original licence link has changed is not relivant.
13028  *
13029  * Fork - LGPL
13030  * <script type="text/javascript">
13031  */
13032
13033 /**
13034  * @class Roo.Resizable
13035  * @extends Roo.util.Observable
13036  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13037  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13038  * 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
13039  * the element will be wrapped for you automatically.</p>
13040  * <p>Here is the list of valid resize handles:</p>
13041  * <pre>
13042 Value   Description
13043 ------  -------------------
13044  'n'     north
13045  's'     south
13046  'e'     east
13047  'w'     west
13048  'nw'    northwest
13049  'sw'    southwest
13050  'se'    southeast
13051  'ne'    northeast
13052  'hd'    horizontal drag
13053  'all'   all
13054 </pre>
13055  * <p>Here's an example showing the creation of a typical Resizable:</p>
13056  * <pre><code>
13057 var resizer = new Roo.Resizable("element-id", {
13058     handles: 'all',
13059     minWidth: 200,
13060     minHeight: 100,
13061     maxWidth: 500,
13062     maxHeight: 400,
13063     pinned: true
13064 });
13065 resizer.on("resize", myHandler);
13066 </code></pre>
13067  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13068  * resizer.east.setDisplayed(false);</p>
13069  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13070  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13071  * resize operation's new size (defaults to [0, 0])
13072  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13073  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13074  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13075  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13076  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13077  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13078  * @cfg {Number} width The width of the element in pixels (defaults to null)
13079  * @cfg {Number} height The height of the element in pixels (defaults to null)
13080  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13081  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13082  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13083  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13084  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13085  * in favor of the handles config option (defaults to false)
13086  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13087  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13088  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13089  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13090  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13091  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13092  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13093  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13094  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13095  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13096  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13097  * @constructor
13098  * Create a new resizable component
13099  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13100  * @param {Object} config configuration options
13101   */
13102 Roo.Resizable = function(el, config)
13103 {
13104     this.el = Roo.get(el);
13105
13106     if(config && config.wrap){
13107         config.resizeChild = this.el;
13108         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13109         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13110         this.el.setStyle("overflow", "hidden");
13111         this.el.setPositioning(config.resizeChild.getPositioning());
13112         config.resizeChild.clearPositioning();
13113         if(!config.width || !config.height){
13114             var csize = config.resizeChild.getSize();
13115             this.el.setSize(csize.width, csize.height);
13116         }
13117         if(config.pinned && !config.adjustments){
13118             config.adjustments = "auto";
13119         }
13120     }
13121
13122     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13123     this.proxy.unselectable();
13124     this.proxy.enableDisplayMode('block');
13125
13126     Roo.apply(this, config);
13127
13128     if(this.pinned){
13129         this.disableTrackOver = true;
13130         this.el.addClass("x-resizable-pinned");
13131     }
13132     // if the element isn't positioned, make it relative
13133     var position = this.el.getStyle("position");
13134     if(position != "absolute" && position != "fixed"){
13135         this.el.setStyle("position", "relative");
13136     }
13137     if(!this.handles){ // no handles passed, must be legacy style
13138         this.handles = 's,e,se';
13139         if(this.multiDirectional){
13140             this.handles += ',n,w';
13141         }
13142     }
13143     if(this.handles == "all"){
13144         this.handles = "n s e w ne nw se sw";
13145     }
13146     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13147     var ps = Roo.Resizable.positions;
13148     for(var i = 0, len = hs.length; i < len; i++){
13149         if(hs[i] && ps[hs[i]]){
13150             var pos = ps[hs[i]];
13151             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13152         }
13153     }
13154     // legacy
13155     this.corner = this.southeast;
13156     
13157     // updateBox = the box can move..
13158     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13159         this.updateBox = true;
13160     }
13161
13162     this.activeHandle = null;
13163
13164     if(this.resizeChild){
13165         if(typeof this.resizeChild == "boolean"){
13166             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13167         }else{
13168             this.resizeChild = Roo.get(this.resizeChild, true);
13169         }
13170     }
13171     
13172     if(this.adjustments == "auto"){
13173         var rc = this.resizeChild;
13174         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13175         if(rc && (hw || hn)){
13176             rc.position("relative");
13177             rc.setLeft(hw ? hw.el.getWidth() : 0);
13178             rc.setTop(hn ? hn.el.getHeight() : 0);
13179         }
13180         this.adjustments = [
13181             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13182             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13183         ];
13184     }
13185
13186     if(this.draggable){
13187         this.dd = this.dynamic ?
13188             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13189         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13190     }
13191
13192     // public events
13193     this.addEvents({
13194         /**
13195          * @event beforeresize
13196          * Fired before resize is allowed. Set enabled to false to cancel resize.
13197          * @param {Roo.Resizable} this
13198          * @param {Roo.EventObject} e The mousedown event
13199          */
13200         "beforeresize" : true,
13201         /**
13202          * @event resize
13203          * Fired after a resize.
13204          * @param {Roo.Resizable} this
13205          * @param {Number} width The new width
13206          * @param {Number} height The new height
13207          * @param {Roo.EventObject} e The mouseup event
13208          */
13209         "resize" : true
13210     });
13211
13212     if(this.width !== null && this.height !== null){
13213         this.resizeTo(this.width, this.height);
13214     }else{
13215         this.updateChildSize();
13216     }
13217     if(Roo.isIE){
13218         this.el.dom.style.zoom = 1;
13219     }
13220     Roo.Resizable.superclass.constructor.call(this);
13221 };
13222
13223 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13224         resizeChild : false,
13225         adjustments : [0, 0],
13226         minWidth : 5,
13227         minHeight : 5,
13228         maxWidth : 10000,
13229         maxHeight : 10000,
13230         enabled : true,
13231         animate : false,
13232         duration : .35,
13233         dynamic : false,
13234         handles : false,
13235         multiDirectional : false,
13236         disableTrackOver : false,
13237         easing : 'easeOutStrong',
13238         widthIncrement : 0,
13239         heightIncrement : 0,
13240         pinned : false,
13241         width : null,
13242         height : null,
13243         preserveRatio : false,
13244         transparent: false,
13245         minX: 0,
13246         minY: 0,
13247         draggable: false,
13248
13249         /**
13250          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13251          */
13252         constrainTo: undefined,
13253         /**
13254          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13255          */
13256         resizeRegion: undefined,
13257
13258
13259     /**
13260      * Perform a manual resize
13261      * @param {Number} width
13262      * @param {Number} height
13263      */
13264     resizeTo : function(width, height){
13265         this.el.setSize(width, height);
13266         this.updateChildSize();
13267         this.fireEvent("resize", this, width, height, null);
13268     },
13269
13270     // private
13271     startSizing : function(e, handle){
13272         this.fireEvent("beforeresize", this, e);
13273         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13274
13275             if(!this.overlay){
13276                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13277                 this.overlay.unselectable();
13278                 this.overlay.enableDisplayMode("block");
13279                 this.overlay.on("mousemove", this.onMouseMove, this);
13280                 this.overlay.on("mouseup", this.onMouseUp, this);
13281             }
13282             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13283
13284             this.resizing = true;
13285             this.startBox = this.el.getBox();
13286             this.startPoint = e.getXY();
13287             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13288                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13289
13290             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13291             this.overlay.show();
13292
13293             if(this.constrainTo) {
13294                 var ct = Roo.get(this.constrainTo);
13295                 this.resizeRegion = ct.getRegion().adjust(
13296                     ct.getFrameWidth('t'),
13297                     ct.getFrameWidth('l'),
13298                     -ct.getFrameWidth('b'),
13299                     -ct.getFrameWidth('r')
13300                 );
13301             }
13302
13303             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13304             this.proxy.show();
13305             this.proxy.setBox(this.startBox);
13306             if(!this.dynamic){
13307                 this.proxy.setStyle('visibility', 'visible');
13308             }
13309         }
13310     },
13311
13312     // private
13313     onMouseDown : function(handle, e){
13314         if(this.enabled){
13315             e.stopEvent();
13316             this.activeHandle = handle;
13317             this.startSizing(e, handle);
13318         }
13319     },
13320
13321     // private
13322     onMouseUp : function(e){
13323         var size = this.resizeElement();
13324         this.resizing = false;
13325         this.handleOut();
13326         this.overlay.hide();
13327         this.proxy.hide();
13328         this.fireEvent("resize", this, size.width, size.height, e);
13329     },
13330
13331     // private
13332     updateChildSize : function(){
13333         if(this.resizeChild){
13334             var el = this.el;
13335             var child = this.resizeChild;
13336             var adj = this.adjustments;
13337             if(el.dom.offsetWidth){
13338                 var b = el.getSize(true);
13339                 child.setSize(b.width+adj[0], b.height+adj[1]);
13340             }
13341             // Second call here for IE
13342             // The first call enables instant resizing and
13343             // the second call corrects scroll bars if they
13344             // exist
13345             if(Roo.isIE){
13346                 setTimeout(function(){
13347                     if(el.dom.offsetWidth){
13348                         var b = el.getSize(true);
13349                         child.setSize(b.width+adj[0], b.height+adj[1]);
13350                     }
13351                 }, 10);
13352             }
13353         }
13354     },
13355
13356     // private
13357     snap : function(value, inc, min){
13358         if(!inc || !value) return value;
13359         var newValue = value;
13360         var m = value % inc;
13361         if(m > 0){
13362             if(m > (inc/2)){
13363                 newValue = value + (inc-m);
13364             }else{
13365                 newValue = value - m;
13366             }
13367         }
13368         return Math.max(min, newValue);
13369     },
13370
13371     // private
13372     resizeElement : function(){
13373         var box = this.proxy.getBox();
13374         if(this.updateBox){
13375             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13376         }else{
13377             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13378         }
13379         this.updateChildSize();
13380         if(!this.dynamic){
13381             this.proxy.hide();
13382         }
13383         return box;
13384     },
13385
13386     // private
13387     constrain : function(v, diff, m, mx){
13388         if(v - diff < m){
13389             diff = v - m;
13390         }else if(v - diff > mx){
13391             diff = mx - v;
13392         }
13393         return diff;
13394     },
13395
13396     // private
13397     onMouseMove : function(e){
13398         if(this.enabled){
13399             try{// try catch so if something goes wrong the user doesn't get hung
13400
13401             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13402                 return;
13403             }
13404
13405             //var curXY = this.startPoint;
13406             var curSize = this.curSize || this.startBox;
13407             var x = this.startBox.x, y = this.startBox.y;
13408             var ox = x, oy = y;
13409             var w = curSize.width, h = curSize.height;
13410             var ow = w, oh = h;
13411             var mw = this.minWidth, mh = this.minHeight;
13412             var mxw = this.maxWidth, mxh = this.maxHeight;
13413             var wi = this.widthIncrement;
13414             var hi = this.heightIncrement;
13415
13416             var eventXY = e.getXY();
13417             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13418             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13419
13420             var pos = this.activeHandle.position;
13421
13422             switch(pos){
13423                 case "east":
13424                     w += diffX;
13425                     w = Math.min(Math.max(mw, w), mxw);
13426                     break;
13427              
13428                 case "south":
13429                     h += diffY;
13430                     h = Math.min(Math.max(mh, h), mxh);
13431                     break;
13432                 case "southeast":
13433                     w += diffX;
13434                     h += diffY;
13435                     w = Math.min(Math.max(mw, w), mxw);
13436                     h = Math.min(Math.max(mh, h), mxh);
13437                     break;
13438                 case "north":
13439                     diffY = this.constrain(h, diffY, mh, mxh);
13440                     y += diffY;
13441                     h -= diffY;
13442                     break;
13443                 case "hdrag":
13444                     
13445                     if (wi) {
13446                         var adiffX = Math.abs(diffX);
13447                         var sub = (adiffX % wi); // how much 
13448                         if (sub > (wi/2)) { // far enough to snap
13449                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13450                         } else {
13451                             // remove difference.. 
13452                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13453                         }
13454                     }
13455                     x += diffX;
13456                     x = Math.max(this.minX, x);
13457                     break;
13458                 case "west":
13459                     diffX = this.constrain(w, diffX, mw, mxw);
13460                     x += diffX;
13461                     w -= diffX;
13462                     break;
13463                 case "northeast":
13464                     w += diffX;
13465                     w = Math.min(Math.max(mw, w), mxw);
13466                     diffY = this.constrain(h, diffY, mh, mxh);
13467                     y += diffY;
13468                     h -= diffY;
13469                     break;
13470                 case "northwest":
13471                     diffX = this.constrain(w, diffX, mw, mxw);
13472                     diffY = this.constrain(h, diffY, mh, mxh);
13473                     y += diffY;
13474                     h -= diffY;
13475                     x += diffX;
13476                     w -= diffX;
13477                     break;
13478                case "southwest":
13479                     diffX = this.constrain(w, diffX, mw, mxw);
13480                     h += diffY;
13481                     h = Math.min(Math.max(mh, h), mxh);
13482                     x += diffX;
13483                     w -= diffX;
13484                     break;
13485             }
13486
13487             var sw = this.snap(w, wi, mw);
13488             var sh = this.snap(h, hi, mh);
13489             if(sw != w || sh != h){
13490                 switch(pos){
13491                     case "northeast":
13492                         y -= sh - h;
13493                     break;
13494                     case "north":
13495                         y -= sh - h;
13496                         break;
13497                     case "southwest":
13498                         x -= sw - w;
13499                     break;
13500                     case "west":
13501                         x -= sw - w;
13502                         break;
13503                     case "northwest":
13504                         x -= sw - w;
13505                         y -= sh - h;
13506                     break;
13507                 }
13508                 w = sw;
13509                 h = sh;
13510             }
13511
13512             if(this.preserveRatio){
13513                 switch(pos){
13514                     case "southeast":
13515                     case "east":
13516                         h = oh * (w/ow);
13517                         h = Math.min(Math.max(mh, h), mxh);
13518                         w = ow * (h/oh);
13519                        break;
13520                     case "south":
13521                         w = ow * (h/oh);
13522                         w = Math.min(Math.max(mw, w), mxw);
13523                         h = oh * (w/ow);
13524                         break;
13525                     case "northeast":
13526                         w = ow * (h/oh);
13527                         w = Math.min(Math.max(mw, w), mxw);
13528                         h = oh * (w/ow);
13529                     break;
13530                     case "north":
13531                         var tw = w;
13532                         w = ow * (h/oh);
13533                         w = Math.min(Math.max(mw, w), mxw);
13534                         h = oh * (w/ow);
13535                         x += (tw - w) / 2;
13536                         break;
13537                     case "southwest":
13538                         h = oh * (w/ow);
13539                         h = Math.min(Math.max(mh, h), mxh);
13540                         var tw = w;
13541                         w = ow * (h/oh);
13542                         x += tw - w;
13543                         break;
13544                     case "west":
13545                         var th = h;
13546                         h = oh * (w/ow);
13547                         h = Math.min(Math.max(mh, h), mxh);
13548                         y += (th - h) / 2;
13549                         var tw = w;
13550                         w = ow * (h/oh);
13551                         x += tw - w;
13552                        break;
13553                     case "northwest":
13554                         var tw = w;
13555                         var th = h;
13556                         h = oh * (w/ow);
13557                         h = Math.min(Math.max(mh, h), mxh);
13558                         w = ow * (h/oh);
13559                         y += th - h;
13560                         x += tw - w;
13561                        break;
13562
13563                 }
13564             }
13565             if (pos == 'hdrag') {
13566                 w = ow;
13567             }
13568             this.proxy.setBounds(x, y, w, h);
13569             if(this.dynamic){
13570                 this.resizeElement();
13571             }
13572             }catch(e){}
13573         }
13574     },
13575
13576     // private
13577     handleOver : function(){
13578         if(this.enabled){
13579             this.el.addClass("x-resizable-over");
13580         }
13581     },
13582
13583     // private
13584     handleOut : function(){
13585         if(!this.resizing){
13586             this.el.removeClass("x-resizable-over");
13587         }
13588     },
13589
13590     /**
13591      * Returns the element this component is bound to.
13592      * @return {Roo.Element}
13593      */
13594     getEl : function(){
13595         return this.el;
13596     },
13597
13598     /**
13599      * Returns the resizeChild element (or null).
13600      * @return {Roo.Element}
13601      */
13602     getResizeChild : function(){
13603         return this.resizeChild;
13604     },
13605
13606     /**
13607      * Destroys this resizable. If the element was wrapped and
13608      * removeEl is not true then the element remains.
13609      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13610      */
13611     destroy : function(removeEl){
13612         this.proxy.remove();
13613         if(this.overlay){
13614             this.overlay.removeAllListeners();
13615             this.overlay.remove();
13616         }
13617         var ps = Roo.Resizable.positions;
13618         for(var k in ps){
13619             if(typeof ps[k] != "function" && this[ps[k]]){
13620                 var h = this[ps[k]];
13621                 h.el.removeAllListeners();
13622                 h.el.remove();
13623             }
13624         }
13625         if(removeEl){
13626             this.el.update("");
13627             this.el.remove();
13628         }
13629     }
13630 });
13631
13632 // private
13633 // hash to map config positions to true positions
13634 Roo.Resizable.positions = {
13635     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13636     hd: "hdrag"
13637 };
13638
13639 // private
13640 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13641     if(!this.tpl){
13642         // only initialize the template if resizable is used
13643         var tpl = Roo.DomHelper.createTemplate(
13644             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13645         );
13646         tpl.compile();
13647         Roo.Resizable.Handle.prototype.tpl = tpl;
13648     }
13649     this.position = pos;
13650     this.rz = rz;
13651     // show north drag fro topdra
13652     var handlepos = pos == 'hdrag' ? 'north' : pos;
13653     
13654     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13655     if (pos == 'hdrag') {
13656         this.el.setStyle('cursor', 'pointer');
13657     }
13658     this.el.unselectable();
13659     if(transparent){
13660         this.el.setOpacity(0);
13661     }
13662     this.el.on("mousedown", this.onMouseDown, this);
13663     if(!disableTrackOver){
13664         this.el.on("mouseover", this.onMouseOver, this);
13665         this.el.on("mouseout", this.onMouseOut, this);
13666     }
13667 };
13668
13669 // private
13670 Roo.Resizable.Handle.prototype = {
13671     afterResize : function(rz){
13672         // do nothing
13673     },
13674     // private
13675     onMouseDown : function(e){
13676         this.rz.onMouseDown(this, e);
13677     },
13678     // private
13679     onMouseOver : function(e){
13680         this.rz.handleOver(this, e);
13681     },
13682     // private
13683     onMouseOut : function(e){
13684         this.rz.handleOut(this, e);
13685     }
13686 };/*
13687  * Based on:
13688  * Ext JS Library 1.1.1
13689  * Copyright(c) 2006-2007, Ext JS, LLC.
13690  *
13691  * Originally Released Under LGPL - original licence link has changed is not relivant.
13692  *
13693  * Fork - LGPL
13694  * <script type="text/javascript">
13695  */
13696
13697 /**
13698  * @class Roo.Editor
13699  * @extends Roo.Component
13700  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13701  * @constructor
13702  * Create a new Editor
13703  * @param {Roo.form.Field} field The Field object (or descendant)
13704  * @param {Object} config The config object
13705  */
13706 Roo.Editor = function(field, config){
13707     Roo.Editor.superclass.constructor.call(this, config);
13708     this.field = field;
13709     this.addEvents({
13710         /**
13711              * @event beforestartedit
13712              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13713              * false from the handler of this event.
13714              * @param {Editor} this
13715              * @param {Roo.Element} boundEl The underlying element bound to this editor
13716              * @param {Mixed} value The field value being set
13717              */
13718         "beforestartedit" : true,
13719         /**
13720              * @event startedit
13721              * Fires when this editor is displayed
13722              * @param {Roo.Element} boundEl The underlying element bound to this editor
13723              * @param {Mixed} value The starting field value
13724              */
13725         "startedit" : true,
13726         /**
13727              * @event beforecomplete
13728              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13729              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13730              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13731              * event will not fire since no edit actually occurred.
13732              * @param {Editor} this
13733              * @param {Mixed} value The current field value
13734              * @param {Mixed} startValue The original field value
13735              */
13736         "beforecomplete" : true,
13737         /**
13738              * @event complete
13739              * Fires after editing is complete and any changed value has been written to the underlying field.
13740              * @param {Editor} this
13741              * @param {Mixed} value The current field value
13742              * @param {Mixed} startValue The original field value
13743              */
13744         "complete" : true,
13745         /**
13746          * @event specialkey
13747          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13748          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13749          * @param {Roo.form.Field} this
13750          * @param {Roo.EventObject} e The event object
13751          */
13752         "specialkey" : true
13753     });
13754 };
13755
13756 Roo.extend(Roo.Editor, Roo.Component, {
13757     /**
13758      * @cfg {Boolean/String} autosize
13759      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13760      * or "height" to adopt the height only (defaults to false)
13761      */
13762     /**
13763      * @cfg {Boolean} revertInvalid
13764      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13765      * validation fails (defaults to true)
13766      */
13767     /**
13768      * @cfg {Boolean} ignoreNoChange
13769      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13770      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13771      * will never be ignored.
13772      */
13773     /**
13774      * @cfg {Boolean} hideEl
13775      * False to keep the bound element visible while the editor is displayed (defaults to true)
13776      */
13777     /**
13778      * @cfg {Mixed} value
13779      * The data value of the underlying field (defaults to "")
13780      */
13781     value : "",
13782     /**
13783      * @cfg {String} alignment
13784      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13785      */
13786     alignment: "c-c?",
13787     /**
13788      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13789      * for bottom-right shadow (defaults to "frame")
13790      */
13791     shadow : "frame",
13792     /**
13793      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13794      */
13795     constrain : false,
13796     /**
13797      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13798      */
13799     completeOnEnter : false,
13800     /**
13801      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13802      */
13803     cancelOnEsc : false,
13804     /**
13805      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13806      */
13807     updateEl : false,
13808
13809     // private
13810     onRender : function(ct, position){
13811         this.el = new Roo.Layer({
13812             shadow: this.shadow,
13813             cls: "x-editor",
13814             parentEl : ct,
13815             shim : this.shim,
13816             shadowOffset:4,
13817             id: this.id,
13818             constrain: this.constrain
13819         });
13820         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13821         if(this.field.msgTarget != 'title'){
13822             this.field.msgTarget = 'qtip';
13823         }
13824         this.field.render(this.el);
13825         if(Roo.isGecko){
13826             this.field.el.dom.setAttribute('autocomplete', 'off');
13827         }
13828         this.field.on("specialkey", this.onSpecialKey, this);
13829         if(this.swallowKeys){
13830             this.field.el.swallowEvent(['keydown','keypress']);
13831         }
13832         this.field.show();
13833         this.field.on("blur", this.onBlur, this);
13834         if(this.field.grow){
13835             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13836         }
13837     },
13838
13839     onSpecialKey : function(field, e){
13840         //Roo.log('editor onSpecialKey');
13841         if(this.completeOnEnter && e.getKey() == e.ENTER){
13842             e.stopEvent();
13843             this.completeEdit();
13844         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13845             this.cancelEdit();
13846         }else{
13847             this.fireEvent('specialkey', field, e);
13848         }
13849     },
13850
13851     /**
13852      * Starts the editing process and shows the editor.
13853      * @param {String/HTMLElement/Element} el The element to edit
13854      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13855       * to the innerHTML of el.
13856      */
13857     startEdit : function(el, value){
13858         if(this.editing){
13859             this.completeEdit();
13860         }
13861         this.boundEl = Roo.get(el);
13862         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13863         if(!this.rendered){
13864             this.render(this.parentEl || document.body);
13865         }
13866         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13867             return;
13868         }
13869         this.startValue = v;
13870         this.field.setValue(v);
13871         if(this.autoSize){
13872             var sz = this.boundEl.getSize();
13873             switch(this.autoSize){
13874                 case "width":
13875                 this.setSize(sz.width,  "");
13876                 break;
13877                 case "height":
13878                 this.setSize("",  sz.height);
13879                 break;
13880                 default:
13881                 this.setSize(sz.width,  sz.height);
13882             }
13883         }
13884         this.el.alignTo(this.boundEl, this.alignment);
13885         this.editing = true;
13886         if(Roo.QuickTips){
13887             Roo.QuickTips.disable();
13888         }
13889         this.show();
13890     },
13891
13892     /**
13893      * Sets the height and width of this editor.
13894      * @param {Number} width The new width
13895      * @param {Number} height The new height
13896      */
13897     setSize : function(w, h){
13898         this.field.setSize(w, h);
13899         if(this.el){
13900             this.el.sync();
13901         }
13902     },
13903
13904     /**
13905      * Realigns the editor to the bound field based on the current alignment config value.
13906      */
13907     realign : function(){
13908         this.el.alignTo(this.boundEl, this.alignment);
13909     },
13910
13911     /**
13912      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13913      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13914      */
13915     completeEdit : function(remainVisible){
13916         if(!this.editing){
13917             return;
13918         }
13919         var v = this.getValue();
13920         if(this.revertInvalid !== false && !this.field.isValid()){
13921             v = this.startValue;
13922             this.cancelEdit(true);
13923         }
13924         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13925             this.editing = false;
13926             this.hide();
13927             return;
13928         }
13929         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13930             this.editing = false;
13931             if(this.updateEl && this.boundEl){
13932                 this.boundEl.update(v);
13933             }
13934             if(remainVisible !== true){
13935                 this.hide();
13936             }
13937             this.fireEvent("complete", this, v, this.startValue);
13938         }
13939     },
13940
13941     // private
13942     onShow : function(){
13943         this.el.show();
13944         if(this.hideEl !== false){
13945             this.boundEl.hide();
13946         }
13947         this.field.show();
13948         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13949             this.fixIEFocus = true;
13950             this.deferredFocus.defer(50, this);
13951         }else{
13952             this.field.focus();
13953         }
13954         this.fireEvent("startedit", this.boundEl, this.startValue);
13955     },
13956
13957     deferredFocus : function(){
13958         if(this.editing){
13959             this.field.focus();
13960         }
13961     },
13962
13963     /**
13964      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13965      * reverted to the original starting value.
13966      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13967      * cancel (defaults to false)
13968      */
13969     cancelEdit : function(remainVisible){
13970         if(this.editing){
13971             this.setValue(this.startValue);
13972             if(remainVisible !== true){
13973                 this.hide();
13974             }
13975         }
13976     },
13977
13978     // private
13979     onBlur : function(){
13980         if(this.allowBlur !== true && this.editing){
13981             this.completeEdit();
13982         }
13983     },
13984
13985     // private
13986     onHide : function(){
13987         if(this.editing){
13988             this.completeEdit();
13989             return;
13990         }
13991         this.field.blur();
13992         if(this.field.collapse){
13993             this.field.collapse();
13994         }
13995         this.el.hide();
13996         if(this.hideEl !== false){
13997             this.boundEl.show();
13998         }
13999         if(Roo.QuickTips){
14000             Roo.QuickTips.enable();
14001         }
14002     },
14003
14004     /**
14005      * Sets the data value of the editor
14006      * @param {Mixed} value Any valid value supported by the underlying field
14007      */
14008     setValue : function(v){
14009         this.field.setValue(v);
14010     },
14011
14012     /**
14013      * Gets the data value of the editor
14014      * @return {Mixed} The data value
14015      */
14016     getValue : function(){
14017         return this.field.getValue();
14018     }
14019 });/*
14020  * Based on:
14021  * Ext JS Library 1.1.1
14022  * Copyright(c) 2006-2007, Ext JS, LLC.
14023  *
14024  * Originally Released Under LGPL - original licence link has changed is not relivant.
14025  *
14026  * Fork - LGPL
14027  * <script type="text/javascript">
14028  */
14029  
14030 /**
14031  * @class Roo.BasicDialog
14032  * @extends Roo.util.Observable
14033  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14034  * <pre><code>
14035 var dlg = new Roo.BasicDialog("my-dlg", {
14036     height: 200,
14037     width: 300,
14038     minHeight: 100,
14039     minWidth: 150,
14040     modal: true,
14041     proxyDrag: true,
14042     shadow: true
14043 });
14044 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14045 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14046 dlg.addButton('Cancel', dlg.hide, dlg);
14047 dlg.show();
14048 </code></pre>
14049   <b>A Dialog should always be a direct child of the body element.</b>
14050  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14051  * @cfg {String} title Default text to display in the title bar (defaults to null)
14052  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14053  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14054  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14055  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14056  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14057  * (defaults to null with no animation)
14058  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14059  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14060  * property for valid values (defaults to 'all')
14061  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14062  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14063  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14064  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14065  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14066  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14067  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14068  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14069  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14070  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14071  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14072  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14073  * draggable = true (defaults to false)
14074  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14075  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14076  * shadow (defaults to false)
14077  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14078  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14079  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14080  * @cfg {Array} buttons Array of buttons
14081  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14082  * @constructor
14083  * Create a new BasicDialog.
14084  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14085  * @param {Object} config Configuration options
14086  */
14087 Roo.BasicDialog = function(el, config){
14088     this.el = Roo.get(el);
14089     var dh = Roo.DomHelper;
14090     if(!this.el && config && config.autoCreate){
14091         if(typeof config.autoCreate == "object"){
14092             if(!config.autoCreate.id){
14093                 config.autoCreate.id = el;
14094             }
14095             this.el = dh.append(document.body,
14096                         config.autoCreate, true);
14097         }else{
14098             this.el = dh.append(document.body,
14099                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14100         }
14101     }
14102     el = this.el;
14103     el.setDisplayed(true);
14104     el.hide = this.hideAction;
14105     this.id = el.id;
14106     el.addClass("x-dlg");
14107
14108     Roo.apply(this, config);
14109
14110     this.proxy = el.createProxy("x-dlg-proxy");
14111     this.proxy.hide = this.hideAction;
14112     this.proxy.setOpacity(.5);
14113     this.proxy.hide();
14114
14115     if(config.width){
14116         el.setWidth(config.width);
14117     }
14118     if(config.height){
14119         el.setHeight(config.height);
14120     }
14121     this.size = el.getSize();
14122     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14123         this.xy = [config.x,config.y];
14124     }else{
14125         this.xy = el.getCenterXY(true);
14126     }
14127     /** The header element @type Roo.Element */
14128     this.header = el.child("> .x-dlg-hd");
14129     /** The body element @type Roo.Element */
14130     this.body = el.child("> .x-dlg-bd");
14131     /** The footer element @type Roo.Element */
14132     this.footer = el.child("> .x-dlg-ft");
14133
14134     if(!this.header){
14135         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14136     }
14137     if(!this.body){
14138         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14139     }
14140
14141     this.header.unselectable();
14142     if(this.title){
14143         this.header.update(this.title);
14144     }
14145     // this element allows the dialog to be focused for keyboard event
14146     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14147     this.focusEl.swallowEvent("click", true);
14148
14149     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14150
14151     // wrap the body and footer for special rendering
14152     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14153     if(this.footer){
14154         this.bwrap.dom.appendChild(this.footer.dom);
14155     }
14156
14157     this.bg = this.el.createChild({
14158         tag: "div", cls:"x-dlg-bg",
14159         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14160     });
14161     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14162
14163
14164     if(this.autoScroll !== false && !this.autoTabs){
14165         this.body.setStyle("overflow", "auto");
14166     }
14167
14168     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14169
14170     if(this.closable !== false){
14171         this.el.addClass("x-dlg-closable");
14172         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14173         this.close.on("click", this.closeClick, this);
14174         this.close.addClassOnOver("x-dlg-close-over");
14175     }
14176     if(this.collapsible !== false){
14177         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14178         this.collapseBtn.on("click", this.collapseClick, this);
14179         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14180         this.header.on("dblclick", this.collapseClick, this);
14181     }
14182     if(this.resizable !== false){
14183         this.el.addClass("x-dlg-resizable");
14184         this.resizer = new Roo.Resizable(el, {
14185             minWidth: this.minWidth || 80,
14186             minHeight:this.minHeight || 80,
14187             handles: this.resizeHandles || "all",
14188             pinned: true
14189         });
14190         this.resizer.on("beforeresize", this.beforeResize, this);
14191         this.resizer.on("resize", this.onResize, this);
14192     }
14193     if(this.draggable !== false){
14194         el.addClass("x-dlg-draggable");
14195         if (!this.proxyDrag) {
14196             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14197         }
14198         else {
14199             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14200         }
14201         dd.setHandleElId(this.header.id);
14202         dd.endDrag = this.endMove.createDelegate(this);
14203         dd.startDrag = this.startMove.createDelegate(this);
14204         dd.onDrag = this.onDrag.createDelegate(this);
14205         dd.scroll = false;
14206         this.dd = dd;
14207     }
14208     if(this.modal){
14209         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14210         this.mask.enableDisplayMode("block");
14211         this.mask.hide();
14212         this.el.addClass("x-dlg-modal");
14213     }
14214     if(this.shadow){
14215         this.shadow = new Roo.Shadow({
14216             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14217             offset : this.shadowOffset
14218         });
14219     }else{
14220         this.shadowOffset = 0;
14221     }
14222     if(Roo.useShims && this.shim !== false){
14223         this.shim = this.el.createShim();
14224         this.shim.hide = this.hideAction;
14225         this.shim.hide();
14226     }else{
14227         this.shim = false;
14228     }
14229     if(this.autoTabs){
14230         this.initTabs();
14231     }
14232     if (this.buttons) { 
14233         var bts= this.buttons;
14234         this.buttons = [];
14235         Roo.each(bts, function(b) {
14236             this.addButton(b);
14237         }, this);
14238     }
14239     
14240     
14241     this.addEvents({
14242         /**
14243          * @event keydown
14244          * Fires when a key is pressed
14245          * @param {Roo.BasicDialog} this
14246          * @param {Roo.EventObject} e
14247          */
14248         "keydown" : true,
14249         /**
14250          * @event move
14251          * Fires when this dialog is moved by the user.
14252          * @param {Roo.BasicDialog} this
14253          * @param {Number} x The new page X
14254          * @param {Number} y The new page Y
14255          */
14256         "move" : true,
14257         /**
14258          * @event resize
14259          * Fires when this dialog is resized by the user.
14260          * @param {Roo.BasicDialog} this
14261          * @param {Number} width The new width
14262          * @param {Number} height The new height
14263          */
14264         "resize" : true,
14265         /**
14266          * @event beforehide
14267          * Fires before this dialog is hidden.
14268          * @param {Roo.BasicDialog} this
14269          */
14270         "beforehide" : true,
14271         /**
14272          * @event hide
14273          * Fires when this dialog is hidden.
14274          * @param {Roo.BasicDialog} this
14275          */
14276         "hide" : true,
14277         /**
14278          * @event beforeshow
14279          * Fires before this dialog is shown.
14280          * @param {Roo.BasicDialog} this
14281          */
14282         "beforeshow" : true,
14283         /**
14284          * @event show
14285          * Fires when this dialog is shown.
14286          * @param {Roo.BasicDialog} this
14287          */
14288         "show" : true
14289     });
14290     el.on("keydown", this.onKeyDown, this);
14291     el.on("mousedown", this.toFront, this);
14292     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14293     this.el.hide();
14294     Roo.DialogManager.register(this);
14295     Roo.BasicDialog.superclass.constructor.call(this);
14296 };
14297
14298 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14299     shadowOffset: Roo.isIE ? 6 : 5,
14300     minHeight: 80,
14301     minWidth: 200,
14302     minButtonWidth: 75,
14303     defaultButton: null,
14304     buttonAlign: "right",
14305     tabTag: 'div',
14306     firstShow: true,
14307
14308     /**
14309      * Sets the dialog title text
14310      * @param {String} text The title text to display
14311      * @return {Roo.BasicDialog} this
14312      */
14313     setTitle : function(text){
14314         this.header.update(text);
14315         return this;
14316     },
14317
14318     // private
14319     closeClick : function(){
14320         this.hide();
14321     },
14322
14323     // private
14324     collapseClick : function(){
14325         this[this.collapsed ? "expand" : "collapse"]();
14326     },
14327
14328     /**
14329      * Collapses the dialog to its minimized state (only the title bar is visible).
14330      * Equivalent to the user clicking the collapse dialog button.
14331      */
14332     collapse : function(){
14333         if(!this.collapsed){
14334             this.collapsed = true;
14335             this.el.addClass("x-dlg-collapsed");
14336             this.restoreHeight = this.el.getHeight();
14337             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14338         }
14339     },
14340
14341     /**
14342      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14343      * clicking the expand dialog button.
14344      */
14345     expand : function(){
14346         if(this.collapsed){
14347             this.collapsed = false;
14348             this.el.removeClass("x-dlg-collapsed");
14349             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14350         }
14351     },
14352
14353     /**
14354      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14355      * @return {Roo.TabPanel} The tabs component
14356      */
14357     initTabs : function(){
14358         var tabs = this.getTabs();
14359         while(tabs.getTab(0)){
14360             tabs.removeTab(0);
14361         }
14362         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14363             var dom = el.dom;
14364             tabs.addTab(Roo.id(dom), dom.title);
14365             dom.title = "";
14366         });
14367         tabs.activate(0);
14368         return tabs;
14369     },
14370
14371     // private
14372     beforeResize : function(){
14373         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14374     },
14375
14376     // private
14377     onResize : function(){
14378         this.refreshSize();
14379         this.syncBodyHeight();
14380         this.adjustAssets();
14381         this.focus();
14382         this.fireEvent("resize", this, this.size.width, this.size.height);
14383     },
14384
14385     // private
14386     onKeyDown : function(e){
14387         if(this.isVisible()){
14388             this.fireEvent("keydown", this, e);
14389         }
14390     },
14391
14392     /**
14393      * Resizes the dialog.
14394      * @param {Number} width
14395      * @param {Number} height
14396      * @return {Roo.BasicDialog} this
14397      */
14398     resizeTo : function(width, height){
14399         this.el.setSize(width, height);
14400         this.size = {width: width, height: height};
14401         this.syncBodyHeight();
14402         if(this.fixedcenter){
14403             this.center();
14404         }
14405         if(this.isVisible()){
14406             this.constrainXY();
14407             this.adjustAssets();
14408         }
14409         this.fireEvent("resize", this, width, height);
14410         return this;
14411     },
14412
14413
14414     /**
14415      * Resizes the dialog to fit the specified content size.
14416      * @param {Number} width
14417      * @param {Number} height
14418      * @return {Roo.BasicDialog} this
14419      */
14420     setContentSize : function(w, h){
14421         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14422         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14423         //if(!this.el.isBorderBox()){
14424             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14425             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14426         //}
14427         if(this.tabs){
14428             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14429             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14430         }
14431         this.resizeTo(w, h);
14432         return this;
14433     },
14434
14435     /**
14436      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14437      * executed in response to a particular key being pressed while the dialog is active.
14438      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14439      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14440      * @param {Function} fn The function to call
14441      * @param {Object} scope (optional) The scope of the function
14442      * @return {Roo.BasicDialog} this
14443      */
14444     addKeyListener : function(key, fn, scope){
14445         var keyCode, shift, ctrl, alt;
14446         if(typeof key == "object" && !(key instanceof Array)){
14447             keyCode = key["key"];
14448             shift = key["shift"];
14449             ctrl = key["ctrl"];
14450             alt = key["alt"];
14451         }else{
14452             keyCode = key;
14453         }
14454         var handler = function(dlg, e){
14455             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14456                 var k = e.getKey();
14457                 if(keyCode instanceof Array){
14458                     for(var i = 0, len = keyCode.length; i < len; i++){
14459                         if(keyCode[i] == k){
14460                           fn.call(scope || window, dlg, k, e);
14461                           return;
14462                         }
14463                     }
14464                 }else{
14465                     if(k == keyCode){
14466                         fn.call(scope || window, dlg, k, e);
14467                     }
14468                 }
14469             }
14470         };
14471         this.on("keydown", handler);
14472         return this;
14473     },
14474
14475     /**
14476      * Returns the TabPanel component (creates it if it doesn't exist).
14477      * Note: If you wish to simply check for the existence of tabs without creating them,
14478      * check for a null 'tabs' property.
14479      * @return {Roo.TabPanel} The tabs component
14480      */
14481     getTabs : function(){
14482         if(!this.tabs){
14483             this.el.addClass("x-dlg-auto-tabs");
14484             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14485             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14486         }
14487         return this.tabs;
14488     },
14489
14490     /**
14491      * Adds a button to the footer section of the dialog.
14492      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14493      * object or a valid Roo.DomHelper element config
14494      * @param {Function} handler The function called when the button is clicked
14495      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14496      * @return {Roo.Button} The new button
14497      */
14498     addButton : function(config, handler, scope){
14499         var dh = Roo.DomHelper;
14500         if(!this.footer){
14501             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14502         }
14503         if(!this.btnContainer){
14504             var tb = this.footer.createChild({
14505
14506                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14507                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14508             }, null, true);
14509             this.btnContainer = tb.firstChild.firstChild.firstChild;
14510         }
14511         var bconfig = {
14512             handler: handler,
14513             scope: scope,
14514             minWidth: this.minButtonWidth,
14515             hideParent:true
14516         };
14517         if(typeof config == "string"){
14518             bconfig.text = config;
14519         }else{
14520             if(config.tag){
14521                 bconfig.dhconfig = config;
14522             }else{
14523                 Roo.apply(bconfig, config);
14524             }
14525         }
14526         var fc = false;
14527         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14528             bconfig.position = Math.max(0, bconfig.position);
14529             fc = this.btnContainer.childNodes[bconfig.position];
14530         }
14531          
14532         var btn = new Roo.Button(
14533             fc ? 
14534                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14535                 : this.btnContainer.appendChild(document.createElement("td")),
14536             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14537             bconfig
14538         );
14539         this.syncBodyHeight();
14540         if(!this.buttons){
14541             /**
14542              * Array of all the buttons that have been added to this dialog via addButton
14543              * @type Array
14544              */
14545             this.buttons = [];
14546         }
14547         this.buttons.push(btn);
14548         return btn;
14549     },
14550
14551     /**
14552      * Sets the default button to be focused when the dialog is displayed.
14553      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14554      * @return {Roo.BasicDialog} this
14555      */
14556     setDefaultButton : function(btn){
14557         this.defaultButton = btn;
14558         return this;
14559     },
14560
14561     // private
14562     getHeaderFooterHeight : function(safe){
14563         var height = 0;
14564         if(this.header){
14565            height += this.header.getHeight();
14566         }
14567         if(this.footer){
14568            var fm = this.footer.getMargins();
14569             height += (this.footer.getHeight()+fm.top+fm.bottom);
14570         }
14571         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14572         height += this.centerBg.getPadding("tb");
14573         return height;
14574     },
14575
14576     // private
14577     syncBodyHeight : function(){
14578         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14579         var height = this.size.height - this.getHeaderFooterHeight(false);
14580         bd.setHeight(height-bd.getMargins("tb"));
14581         var hh = this.header.getHeight();
14582         var h = this.size.height-hh;
14583         cb.setHeight(h);
14584         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14585         bw.setHeight(h-cb.getPadding("tb"));
14586         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14587         bd.setWidth(bw.getWidth(true));
14588         if(this.tabs){
14589             this.tabs.syncHeight();
14590             if(Roo.isIE){
14591                 this.tabs.el.repaint();
14592             }
14593         }
14594     },
14595
14596     /**
14597      * Restores the previous state of the dialog if Roo.state is configured.
14598      * @return {Roo.BasicDialog} this
14599      */
14600     restoreState : function(){
14601         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14602         if(box && box.width){
14603             this.xy = [box.x, box.y];
14604             this.resizeTo(box.width, box.height);
14605         }
14606         return this;
14607     },
14608
14609     // private
14610     beforeShow : function(){
14611         this.expand();
14612         if(this.fixedcenter){
14613             this.xy = this.el.getCenterXY(true);
14614         }
14615         if(this.modal){
14616             Roo.get(document.body).addClass("x-body-masked");
14617             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14618             this.mask.show();
14619         }
14620         this.constrainXY();
14621     },
14622
14623     // private
14624     animShow : function(){
14625         var b = Roo.get(this.animateTarget).getBox();
14626         this.proxy.setSize(b.width, b.height);
14627         this.proxy.setLocation(b.x, b.y);
14628         this.proxy.show();
14629         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14630                     true, .35, this.showEl.createDelegate(this));
14631     },
14632
14633     /**
14634      * Shows the dialog.
14635      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14636      * @return {Roo.BasicDialog} this
14637      */
14638     show : function(animateTarget){
14639         if (this.fireEvent("beforeshow", this) === false){
14640             return;
14641         }
14642         if(this.syncHeightBeforeShow){
14643             this.syncBodyHeight();
14644         }else if(this.firstShow){
14645             this.firstShow = false;
14646             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14647         }
14648         this.animateTarget = animateTarget || this.animateTarget;
14649         if(!this.el.isVisible()){
14650             this.beforeShow();
14651             if(this.animateTarget && Roo.get(this.animateTarget)){
14652                 this.animShow();
14653             }else{
14654                 this.showEl();
14655             }
14656         }
14657         return this;
14658     },
14659
14660     // private
14661     showEl : function(){
14662         this.proxy.hide();
14663         this.el.setXY(this.xy);
14664         this.el.show();
14665         this.adjustAssets(true);
14666         this.toFront();
14667         this.focus();
14668         // IE peekaboo bug - fix found by Dave Fenwick
14669         if(Roo.isIE){
14670             this.el.repaint();
14671         }
14672         this.fireEvent("show", this);
14673     },
14674
14675     /**
14676      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14677      * dialog itself will receive focus.
14678      */
14679     focus : function(){
14680         if(this.defaultButton){
14681             this.defaultButton.focus();
14682         }else{
14683             this.focusEl.focus();
14684         }
14685     },
14686
14687     // private
14688     constrainXY : function(){
14689         if(this.constraintoviewport !== false){
14690             if(!this.viewSize){
14691                 if(this.container){
14692                     var s = this.container.getSize();
14693                     this.viewSize = [s.width, s.height];
14694                 }else{
14695                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14696                 }
14697             }
14698             var s = Roo.get(this.container||document).getScroll();
14699
14700             var x = this.xy[0], y = this.xy[1];
14701             var w = this.size.width, h = this.size.height;
14702             var vw = this.viewSize[0], vh = this.viewSize[1];
14703             // only move it if it needs it
14704             var moved = false;
14705             // first validate right/bottom
14706             if(x + w > vw+s.left){
14707                 x = vw - w;
14708                 moved = true;
14709             }
14710             if(y + h > vh+s.top){
14711                 y = vh - h;
14712                 moved = true;
14713             }
14714             // then make sure top/left isn't negative
14715             if(x < s.left){
14716                 x = s.left;
14717                 moved = true;
14718             }
14719             if(y < s.top){
14720                 y = s.top;
14721                 moved = true;
14722             }
14723             if(moved){
14724                 // cache xy
14725                 this.xy = [x, y];
14726                 if(this.isVisible()){
14727                     this.el.setLocation(x, y);
14728                     this.adjustAssets();
14729                 }
14730             }
14731         }
14732     },
14733
14734     // private
14735     onDrag : function(){
14736         if(!this.proxyDrag){
14737             this.xy = this.el.getXY();
14738             this.adjustAssets();
14739         }
14740     },
14741
14742     // private
14743     adjustAssets : function(doShow){
14744         var x = this.xy[0], y = this.xy[1];
14745         var w = this.size.width, h = this.size.height;
14746         if(doShow === true){
14747             if(this.shadow){
14748                 this.shadow.show(this.el);
14749             }
14750             if(this.shim){
14751                 this.shim.show();
14752             }
14753         }
14754         if(this.shadow && this.shadow.isVisible()){
14755             this.shadow.show(this.el);
14756         }
14757         if(this.shim && this.shim.isVisible()){
14758             this.shim.setBounds(x, y, w, h);
14759         }
14760     },
14761
14762     // private
14763     adjustViewport : function(w, h){
14764         if(!w || !h){
14765             w = Roo.lib.Dom.getViewWidth();
14766             h = Roo.lib.Dom.getViewHeight();
14767         }
14768         // cache the size
14769         this.viewSize = [w, h];
14770         if(this.modal && this.mask.isVisible()){
14771             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14772             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14773         }
14774         if(this.isVisible()){
14775             this.constrainXY();
14776         }
14777     },
14778
14779     /**
14780      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14781      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14782      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14783      */
14784     destroy : function(removeEl){
14785         if(this.isVisible()){
14786             this.animateTarget = null;
14787             this.hide();
14788         }
14789         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14790         if(this.tabs){
14791             this.tabs.destroy(removeEl);
14792         }
14793         Roo.destroy(
14794              this.shim,
14795              this.proxy,
14796              this.resizer,
14797              this.close,
14798              this.mask
14799         );
14800         if(this.dd){
14801             this.dd.unreg();
14802         }
14803         if(this.buttons){
14804            for(var i = 0, len = this.buttons.length; i < len; i++){
14805                this.buttons[i].destroy();
14806            }
14807         }
14808         this.el.removeAllListeners();
14809         if(removeEl === true){
14810             this.el.update("");
14811             this.el.remove();
14812         }
14813         Roo.DialogManager.unregister(this);
14814     },
14815
14816     // private
14817     startMove : function(){
14818         if(this.proxyDrag){
14819             this.proxy.show();
14820         }
14821         if(this.constraintoviewport !== false){
14822             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14823         }
14824     },
14825
14826     // private
14827     endMove : function(){
14828         if(!this.proxyDrag){
14829             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14830         }else{
14831             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14832             this.proxy.hide();
14833         }
14834         this.refreshSize();
14835         this.adjustAssets();
14836         this.focus();
14837         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14838     },
14839
14840     /**
14841      * Brings this dialog to the front of any other visible dialogs
14842      * @return {Roo.BasicDialog} this
14843      */
14844     toFront : function(){
14845         Roo.DialogManager.bringToFront(this);
14846         return this;
14847     },
14848
14849     /**
14850      * Sends this dialog to the back (under) of any other visible dialogs
14851      * @return {Roo.BasicDialog} this
14852      */
14853     toBack : function(){
14854         Roo.DialogManager.sendToBack(this);
14855         return this;
14856     },
14857
14858     /**
14859      * Centers this dialog in the viewport
14860      * @return {Roo.BasicDialog} this
14861      */
14862     center : function(){
14863         var xy = this.el.getCenterXY(true);
14864         this.moveTo(xy[0], xy[1]);
14865         return this;
14866     },
14867
14868     /**
14869      * Moves the dialog's top-left corner to the specified point
14870      * @param {Number} x
14871      * @param {Number} y
14872      * @return {Roo.BasicDialog} this
14873      */
14874     moveTo : function(x, y){
14875         this.xy = [x,y];
14876         if(this.isVisible()){
14877             this.el.setXY(this.xy);
14878             this.adjustAssets();
14879         }
14880         return this;
14881     },
14882
14883     /**
14884      * Aligns the dialog to the specified element
14885      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14886      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14887      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14888      * @return {Roo.BasicDialog} this
14889      */
14890     alignTo : function(element, position, offsets){
14891         this.xy = this.el.getAlignToXY(element, position, offsets);
14892         if(this.isVisible()){
14893             this.el.setXY(this.xy);
14894             this.adjustAssets();
14895         }
14896         return this;
14897     },
14898
14899     /**
14900      * Anchors an element to another element and realigns it when the window is resized.
14901      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14902      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14903      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14904      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14905      * is a number, it is used as the buffer delay (defaults to 50ms).
14906      * @return {Roo.BasicDialog} this
14907      */
14908     anchorTo : function(el, alignment, offsets, monitorScroll){
14909         var action = function(){
14910             this.alignTo(el, alignment, offsets);
14911         };
14912         Roo.EventManager.onWindowResize(action, this);
14913         var tm = typeof monitorScroll;
14914         if(tm != 'undefined'){
14915             Roo.EventManager.on(window, 'scroll', action, this,
14916                 {buffer: tm == 'number' ? monitorScroll : 50});
14917         }
14918         action.call(this);
14919         return this;
14920     },
14921
14922     /**
14923      * Returns true if the dialog is visible
14924      * @return {Boolean}
14925      */
14926     isVisible : function(){
14927         return this.el.isVisible();
14928     },
14929
14930     // private
14931     animHide : function(callback){
14932         var b = Roo.get(this.animateTarget).getBox();
14933         this.proxy.show();
14934         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14935         this.el.hide();
14936         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14937                     this.hideEl.createDelegate(this, [callback]));
14938     },
14939
14940     /**
14941      * Hides the dialog.
14942      * @param {Function} callback (optional) Function to call when the dialog is hidden
14943      * @return {Roo.BasicDialog} this
14944      */
14945     hide : function(callback){
14946         if (this.fireEvent("beforehide", this) === false){
14947             return;
14948         }
14949         if(this.shadow){
14950             this.shadow.hide();
14951         }
14952         if(this.shim) {
14953           this.shim.hide();
14954         }
14955         // sometimes animateTarget seems to get set.. causing problems...
14956         // this just double checks..
14957         if(this.animateTarget && Roo.get(this.animateTarget)) {
14958            this.animHide(callback);
14959         }else{
14960             this.el.hide();
14961             this.hideEl(callback);
14962         }
14963         return this;
14964     },
14965
14966     // private
14967     hideEl : function(callback){
14968         this.proxy.hide();
14969         if(this.modal){
14970             this.mask.hide();
14971             Roo.get(document.body).removeClass("x-body-masked");
14972         }
14973         this.fireEvent("hide", this);
14974         if(typeof callback == "function"){
14975             callback();
14976         }
14977     },
14978
14979     // private
14980     hideAction : function(){
14981         this.setLeft("-10000px");
14982         this.setTop("-10000px");
14983         this.setStyle("visibility", "hidden");
14984     },
14985
14986     // private
14987     refreshSize : function(){
14988         this.size = this.el.getSize();
14989         this.xy = this.el.getXY();
14990         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14991     },
14992
14993     // private
14994     // z-index is managed by the DialogManager and may be overwritten at any time
14995     setZIndex : function(index){
14996         if(this.modal){
14997             this.mask.setStyle("z-index", index);
14998         }
14999         if(this.shim){
15000             this.shim.setStyle("z-index", ++index);
15001         }
15002         if(this.shadow){
15003             this.shadow.setZIndex(++index);
15004         }
15005         this.el.setStyle("z-index", ++index);
15006         if(this.proxy){
15007             this.proxy.setStyle("z-index", ++index);
15008         }
15009         if(this.resizer){
15010             this.resizer.proxy.setStyle("z-index", ++index);
15011         }
15012
15013         this.lastZIndex = index;
15014     },
15015
15016     /**
15017      * Returns the element for this dialog
15018      * @return {Roo.Element} The underlying dialog Element
15019      */
15020     getEl : function(){
15021         return this.el;
15022     }
15023 });
15024
15025 /**
15026  * @class Roo.DialogManager
15027  * Provides global access to BasicDialogs that have been created and
15028  * support for z-indexing (layering) multiple open dialogs.
15029  */
15030 Roo.DialogManager = function(){
15031     var list = {};
15032     var accessList = [];
15033     var front = null;
15034
15035     // private
15036     var sortDialogs = function(d1, d2){
15037         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15038     };
15039
15040     // private
15041     var orderDialogs = function(){
15042         accessList.sort(sortDialogs);
15043         var seed = Roo.DialogManager.zseed;
15044         for(var i = 0, len = accessList.length; i < len; i++){
15045             var dlg = accessList[i];
15046             if(dlg){
15047                 dlg.setZIndex(seed + (i*10));
15048             }
15049         }
15050     };
15051
15052     return {
15053         /**
15054          * The starting z-index for BasicDialogs (defaults to 9000)
15055          * @type Number The z-index value
15056          */
15057         zseed : 9000,
15058
15059         // private
15060         register : function(dlg){
15061             list[dlg.id] = dlg;
15062             accessList.push(dlg);
15063         },
15064
15065         // private
15066         unregister : function(dlg){
15067             delete list[dlg.id];
15068             var i=0;
15069             var len=0;
15070             if(!accessList.indexOf){
15071                 for(  i = 0, len = accessList.length; i < len; i++){
15072                     if(accessList[i] == dlg){
15073                         accessList.splice(i, 1);
15074                         return;
15075                     }
15076                 }
15077             }else{
15078                  i = accessList.indexOf(dlg);
15079                 if(i != -1){
15080                     accessList.splice(i, 1);
15081                 }
15082             }
15083         },
15084
15085         /**
15086          * Gets a registered dialog by id
15087          * @param {String/Object} id The id of the dialog or a dialog
15088          * @return {Roo.BasicDialog} this
15089          */
15090         get : function(id){
15091             return typeof id == "object" ? id : list[id];
15092         },
15093
15094         /**
15095          * Brings the specified dialog to the front
15096          * @param {String/Object} dlg The id of the dialog or a dialog
15097          * @return {Roo.BasicDialog} this
15098          */
15099         bringToFront : function(dlg){
15100             dlg = this.get(dlg);
15101             if(dlg != front){
15102                 front = dlg;
15103                 dlg._lastAccess = new Date().getTime();
15104                 orderDialogs();
15105             }
15106             return dlg;
15107         },
15108
15109         /**
15110          * Sends the specified dialog to the back
15111          * @param {String/Object} dlg The id of the dialog or a dialog
15112          * @return {Roo.BasicDialog} this
15113          */
15114         sendToBack : function(dlg){
15115             dlg = this.get(dlg);
15116             dlg._lastAccess = -(new Date().getTime());
15117             orderDialogs();
15118             return dlg;
15119         },
15120
15121         /**
15122          * Hides all dialogs
15123          */
15124         hideAll : function(){
15125             for(var id in list){
15126                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15127                     list[id].hide();
15128                 }
15129             }
15130         }
15131     };
15132 }();
15133
15134 /**
15135  * @class Roo.LayoutDialog
15136  * @extends Roo.BasicDialog
15137  * Dialog which provides adjustments for working with a layout in a Dialog.
15138  * Add your necessary layout config options to the dialog's config.<br>
15139  * Example usage (including a nested layout):
15140  * <pre><code>
15141 if(!dialog){
15142     dialog = new Roo.LayoutDialog("download-dlg", {
15143         modal: true,
15144         width:600,
15145         height:450,
15146         shadow:true,
15147         minWidth:500,
15148         minHeight:350,
15149         autoTabs:true,
15150         proxyDrag:true,
15151         // layout config merges with the dialog config
15152         center:{
15153             tabPosition: "top",
15154             alwaysShowTabs: true
15155         }
15156     });
15157     dialog.addKeyListener(27, dialog.hide, dialog);
15158     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15159     dialog.addButton("Build It!", this.getDownload, this);
15160
15161     // we can even add nested layouts
15162     var innerLayout = new Roo.BorderLayout("dl-inner", {
15163         east: {
15164             initialSize: 200,
15165             autoScroll:true,
15166             split:true
15167         },
15168         center: {
15169             autoScroll:true
15170         }
15171     });
15172     innerLayout.beginUpdate();
15173     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15174     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15175     innerLayout.endUpdate(true);
15176
15177     var layout = dialog.getLayout();
15178     layout.beginUpdate();
15179     layout.add("center", new Roo.ContentPanel("standard-panel",
15180                         {title: "Download the Source", fitToFrame:true}));
15181     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15182                {title: "Build your own roo.js"}));
15183     layout.getRegion("center").showPanel(sp);
15184     layout.endUpdate();
15185 }
15186 </code></pre>
15187     * @constructor
15188     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15189     * @param {Object} config configuration options
15190   */
15191 Roo.LayoutDialog = function(el, cfg){
15192     
15193     var config=  cfg;
15194     if (typeof(cfg) == 'undefined') {
15195         config = Roo.apply({}, el);
15196         // not sure why we use documentElement here.. - it should always be body.
15197         // IE7 borks horribly if we use documentElement.
15198         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15199         //config.autoCreate = true;
15200     }
15201     
15202     
15203     config.autoTabs = false;
15204     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15205     this.body.setStyle({overflow:"hidden", position:"relative"});
15206     this.layout = new Roo.BorderLayout(this.body.dom, config);
15207     this.layout.monitorWindowResize = false;
15208     this.el.addClass("x-dlg-auto-layout");
15209     // fix case when center region overwrites center function
15210     this.center = Roo.BasicDialog.prototype.center;
15211     this.on("show", this.layout.layout, this.layout, true);
15212     if (config.items) {
15213         var xitems = config.items;
15214         delete config.items;
15215         Roo.each(xitems, this.addxtype, this);
15216     }
15217     
15218     
15219 };
15220 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15221     /**
15222      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15223      * @deprecated
15224      */
15225     endUpdate : function(){
15226         this.layout.endUpdate();
15227     },
15228
15229     /**
15230      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15231      *  @deprecated
15232      */
15233     beginUpdate : function(){
15234         this.layout.beginUpdate();
15235     },
15236
15237     /**
15238      * Get the BorderLayout for this dialog
15239      * @return {Roo.BorderLayout}
15240      */
15241     getLayout : function(){
15242         return this.layout;
15243     },
15244
15245     showEl : function(){
15246         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15247         if(Roo.isIE7){
15248             this.layout.layout();
15249         }
15250     },
15251
15252     // private
15253     // Use the syncHeightBeforeShow config option to control this automatically
15254     syncBodyHeight : function(){
15255         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15256         if(this.layout){this.layout.layout();}
15257     },
15258     
15259       /**
15260      * Add an xtype element (actually adds to the layout.)
15261      * @return {Object} xdata xtype object data.
15262      */
15263     
15264     addxtype : function(c) {
15265         return this.layout.addxtype(c);
15266     }
15267 });/*
15268  * Based on:
15269  * Ext JS Library 1.1.1
15270  * Copyright(c) 2006-2007, Ext JS, LLC.
15271  *
15272  * Originally Released Under LGPL - original licence link has changed is not relivant.
15273  *
15274  * Fork - LGPL
15275  * <script type="text/javascript">
15276  */
15277  
15278 /**
15279  * @class Roo.MessageBox
15280  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15281  * Example usage:
15282  *<pre><code>
15283 // Basic alert:
15284 Roo.Msg.alert('Status', 'Changes saved successfully.');
15285
15286 // Prompt for user data:
15287 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15288     if (btn == 'ok'){
15289         // process text value...
15290     }
15291 });
15292
15293 // Show a dialog using config options:
15294 Roo.Msg.show({
15295    title:'Save Changes?',
15296    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15297    buttons: Roo.Msg.YESNOCANCEL,
15298    fn: processResult,
15299    animEl: 'elId'
15300 });
15301 </code></pre>
15302  * @singleton
15303  */
15304 Roo.MessageBox = function(){
15305     var dlg, opt, mask, waitTimer;
15306     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15307     var buttons, activeTextEl, bwidth;
15308
15309     // private
15310     var handleButton = function(button){
15311         dlg.hide();
15312         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15313     };
15314
15315     // private
15316     var handleHide = function(){
15317         if(opt && opt.cls){
15318             dlg.el.removeClass(opt.cls);
15319         }
15320         if(waitTimer){
15321             Roo.TaskMgr.stop(waitTimer);
15322             waitTimer = null;
15323         }
15324     };
15325
15326     // private
15327     var updateButtons = function(b){
15328         var width = 0;
15329         if(!b){
15330             buttons["ok"].hide();
15331             buttons["cancel"].hide();
15332             buttons["yes"].hide();
15333             buttons["no"].hide();
15334             dlg.footer.dom.style.display = 'none';
15335             return width;
15336         }
15337         dlg.footer.dom.style.display = '';
15338         for(var k in buttons){
15339             if(typeof buttons[k] != "function"){
15340                 if(b[k]){
15341                     buttons[k].show();
15342                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15343                     width += buttons[k].el.getWidth()+15;
15344                 }else{
15345                     buttons[k].hide();
15346                 }
15347             }
15348         }
15349         return width;
15350     };
15351
15352     // private
15353     var handleEsc = function(d, k, e){
15354         if(opt && opt.closable !== false){
15355             dlg.hide();
15356         }
15357         if(e){
15358             e.stopEvent();
15359         }
15360     };
15361
15362     return {
15363         /**
15364          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15365          * @return {Roo.BasicDialog} The BasicDialog element
15366          */
15367         getDialog : function(){
15368            if(!dlg){
15369                 dlg = new Roo.BasicDialog("x-msg-box", {
15370                     autoCreate : true,
15371                     shadow: true,
15372                     draggable: true,
15373                     resizable:false,
15374                     constraintoviewport:false,
15375                     fixedcenter:true,
15376                     collapsible : false,
15377                     shim:true,
15378                     modal: true,
15379                     width:400, height:100,
15380                     buttonAlign:"center",
15381                     closeClick : function(){
15382                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15383                             handleButton("no");
15384                         }else{
15385                             handleButton("cancel");
15386                         }
15387                     }
15388                 });
15389                 dlg.on("hide", handleHide);
15390                 mask = dlg.mask;
15391                 dlg.addKeyListener(27, handleEsc);
15392                 buttons = {};
15393                 var bt = this.buttonText;
15394                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15395                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15396                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15397                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15398                 bodyEl = dlg.body.createChild({
15399
15400                     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>'
15401                 });
15402                 msgEl = bodyEl.dom.firstChild;
15403                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15404                 textboxEl.enableDisplayMode();
15405                 textboxEl.addKeyListener([10,13], function(){
15406                     if(dlg.isVisible() && opt && opt.buttons){
15407                         if(opt.buttons.ok){
15408                             handleButton("ok");
15409                         }else if(opt.buttons.yes){
15410                             handleButton("yes");
15411                         }
15412                     }
15413                 });
15414                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15415                 textareaEl.enableDisplayMode();
15416                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15417                 progressEl.enableDisplayMode();
15418                 var pf = progressEl.dom.firstChild;
15419                 if (pf) {
15420                     pp = Roo.get(pf.firstChild);
15421                     pp.setHeight(pf.offsetHeight);
15422                 }
15423                 
15424             }
15425             return dlg;
15426         },
15427
15428         /**
15429          * Updates the message box body text
15430          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15431          * the XHTML-compliant non-breaking space character '&amp;#160;')
15432          * @return {Roo.MessageBox} This message box
15433          */
15434         updateText : function(text){
15435             if(!dlg.isVisible() && !opt.width){
15436                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15437             }
15438             msgEl.innerHTML = text || '&#160;';
15439             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15440                         Math.max(opt.minWidth || this.minWidth, bwidth));
15441             if(opt.prompt){
15442                 activeTextEl.setWidth(w);
15443             }
15444             if(dlg.isVisible()){
15445                 dlg.fixedcenter = false;
15446             }
15447             dlg.setContentSize(w, bodyEl.getHeight());
15448             if(dlg.isVisible()){
15449                 dlg.fixedcenter = true;
15450             }
15451             return this;
15452         },
15453
15454         /**
15455          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15456          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15457          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15458          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15459          * @return {Roo.MessageBox} This message box
15460          */
15461         updateProgress : function(value, text){
15462             if(text){
15463                 this.updateText(text);
15464             }
15465             if (pp) { // weird bug on my firefox - for some reason this is not defined
15466                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15467             }
15468             return this;
15469         },        
15470
15471         /**
15472          * Returns true if the message box is currently displayed
15473          * @return {Boolean} True if the message box is visible, else false
15474          */
15475         isVisible : function(){
15476             return dlg && dlg.isVisible();  
15477         },
15478
15479         /**
15480          * Hides the message box if it is displayed
15481          */
15482         hide : function(){
15483             if(this.isVisible()){
15484                 dlg.hide();
15485             }  
15486         },
15487
15488         /**
15489          * Displays a new message box, or reinitializes an existing message box, based on the config options
15490          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15491          * The following config object properties are supported:
15492          * <pre>
15493 Property    Type             Description
15494 ----------  ---------------  ------------------------------------------------------------------------------------
15495 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15496                                    closes (defaults to undefined)
15497 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15498                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15499 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15500                                    progress and wait dialogs will ignore this property and always hide the
15501                                    close button as they can only be closed programmatically.
15502 cls               String           A custom CSS class to apply to the message box element
15503 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15504                                    displayed (defaults to 75)
15505 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15506                                    function will be btn (the name of the button that was clicked, if applicable,
15507                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15508                                    Progress and wait dialogs will ignore this option since they do not respond to
15509                                    user actions and can only be closed programmatically, so any required function
15510                                    should be called by the same code after it closes the dialog.
15511 icon              String           A CSS class that provides a background image to be used as an icon for
15512                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15513 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15514 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15515 modal             Boolean          False to allow user interaction with the page while the message box is
15516                                    displayed (defaults to true)
15517 msg               String           A string that will replace the existing message box body text (defaults
15518                                    to the XHTML-compliant non-breaking space character '&#160;')
15519 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15520 progress          Boolean          True to display a progress bar (defaults to false)
15521 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15522 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15523 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15524 title             String           The title text
15525 value             String           The string value to set into the active textbox element if displayed
15526 wait              Boolean          True to display a progress bar (defaults to false)
15527 width             Number           The width of the dialog in pixels
15528 </pre>
15529          *
15530          * Example usage:
15531          * <pre><code>
15532 Roo.Msg.show({
15533    title: 'Address',
15534    msg: 'Please enter your address:',
15535    width: 300,
15536    buttons: Roo.MessageBox.OKCANCEL,
15537    multiline: true,
15538    fn: saveAddress,
15539    animEl: 'addAddressBtn'
15540 });
15541 </code></pre>
15542          * @param {Object} config Configuration options
15543          * @return {Roo.MessageBox} This message box
15544          */
15545         show : function(options){
15546             if(this.isVisible()){
15547                 this.hide();
15548             }
15549             var d = this.getDialog();
15550             opt = options;
15551             d.setTitle(opt.title || "&#160;");
15552             d.close.setDisplayed(opt.closable !== false);
15553             activeTextEl = textboxEl;
15554             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15555             if(opt.prompt){
15556                 if(opt.multiline){
15557                     textboxEl.hide();
15558                     textareaEl.show();
15559                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15560                         opt.multiline : this.defaultTextHeight);
15561                     activeTextEl = textareaEl;
15562                 }else{
15563                     textboxEl.show();
15564                     textareaEl.hide();
15565                 }
15566             }else{
15567                 textboxEl.hide();
15568                 textareaEl.hide();
15569             }
15570             progressEl.setDisplayed(opt.progress === true);
15571             this.updateProgress(0);
15572             activeTextEl.dom.value = opt.value || "";
15573             if(opt.prompt){
15574                 dlg.setDefaultButton(activeTextEl);
15575             }else{
15576                 var bs = opt.buttons;
15577                 var db = null;
15578                 if(bs && bs.ok){
15579                     db = buttons["ok"];
15580                 }else if(bs && bs.yes){
15581                     db = buttons["yes"];
15582                 }
15583                 dlg.setDefaultButton(db);
15584             }
15585             bwidth = updateButtons(opt.buttons);
15586             this.updateText(opt.msg);
15587             if(opt.cls){
15588                 d.el.addClass(opt.cls);
15589             }
15590             d.proxyDrag = opt.proxyDrag === true;
15591             d.modal = opt.modal !== false;
15592             d.mask = opt.modal !== false ? mask : false;
15593             if(!d.isVisible()){
15594                 // force it to the end of the z-index stack so it gets a cursor in FF
15595                 document.body.appendChild(dlg.el.dom);
15596                 d.animateTarget = null;
15597                 d.show(options.animEl);
15598             }
15599             return this;
15600         },
15601
15602         /**
15603          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15604          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15605          * and closing the message box when the process is complete.
15606          * @param {String} title The title bar text
15607          * @param {String} msg The message box body text
15608          * @return {Roo.MessageBox} This message box
15609          */
15610         progress : function(title, msg){
15611             this.show({
15612                 title : title,
15613                 msg : msg,
15614                 buttons: false,
15615                 progress:true,
15616                 closable:false,
15617                 minWidth: this.minProgressWidth,
15618                 modal : true
15619             });
15620             return this;
15621         },
15622
15623         /**
15624          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15625          * If a callback function is passed it will be called after the user clicks the button, and the
15626          * id of the button that was clicked will be passed as the only parameter to the callback
15627          * (could also be the top-right close button).
15628          * @param {String} title The title bar text
15629          * @param {String} msg The message box body text
15630          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15631          * @param {Object} scope (optional) The scope of the callback function
15632          * @return {Roo.MessageBox} This message box
15633          */
15634         alert : function(title, msg, fn, scope){
15635             this.show({
15636                 title : title,
15637                 msg : msg,
15638                 buttons: this.OK,
15639                 fn: fn,
15640                 scope : scope,
15641                 modal : true
15642             });
15643             return this;
15644         },
15645
15646         /**
15647          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15648          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15649          * You are responsible for closing the message box when the process is complete.
15650          * @param {String} msg The message box body text
15651          * @param {String} title (optional) The title bar text
15652          * @return {Roo.MessageBox} This message box
15653          */
15654         wait : function(msg, title){
15655             this.show({
15656                 title : title,
15657                 msg : msg,
15658                 buttons: false,
15659                 closable:false,
15660                 progress:true,
15661                 modal:true,
15662                 width:300,
15663                 wait:true
15664             });
15665             waitTimer = Roo.TaskMgr.start({
15666                 run: function(i){
15667                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15668                 },
15669                 interval: 1000
15670             });
15671             return this;
15672         },
15673
15674         /**
15675          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15676          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15677          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15678          * @param {String} title The title bar text
15679          * @param {String} msg The message box body text
15680          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15681          * @param {Object} scope (optional) The scope of the callback function
15682          * @return {Roo.MessageBox} This message box
15683          */
15684         confirm : function(title, msg, fn, scope){
15685             this.show({
15686                 title : title,
15687                 msg : msg,
15688                 buttons: this.YESNO,
15689                 fn: fn,
15690                 scope : scope,
15691                 modal : true
15692             });
15693             return this;
15694         },
15695
15696         /**
15697          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15698          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15699          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15700          * (could also be the top-right close button) and the text that was entered will be passed as the two
15701          * parameters to the callback.
15702          * @param {String} title The title bar text
15703          * @param {String} msg The message box body text
15704          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15705          * @param {Object} scope (optional) The scope of the callback function
15706          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15707          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15708          * @return {Roo.MessageBox} This message box
15709          */
15710         prompt : function(title, msg, fn, scope, multiline){
15711             this.show({
15712                 title : title,
15713                 msg : msg,
15714                 buttons: this.OKCANCEL,
15715                 fn: fn,
15716                 minWidth:250,
15717                 scope : scope,
15718                 prompt:true,
15719                 multiline: multiline,
15720                 modal : true
15721             });
15722             return this;
15723         },
15724
15725         /**
15726          * Button config that displays a single OK button
15727          * @type Object
15728          */
15729         OK : {ok:true},
15730         /**
15731          * Button config that displays Yes and No buttons
15732          * @type Object
15733          */
15734         YESNO : {yes:true, no:true},
15735         /**
15736          * Button config that displays OK and Cancel buttons
15737          * @type Object
15738          */
15739         OKCANCEL : {ok:true, cancel:true},
15740         /**
15741          * Button config that displays Yes, No and Cancel buttons
15742          * @type Object
15743          */
15744         YESNOCANCEL : {yes:true, no:true, cancel:true},
15745
15746         /**
15747          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15748          * @type Number
15749          */
15750         defaultTextHeight : 75,
15751         /**
15752          * The maximum width in pixels of the message box (defaults to 600)
15753          * @type Number
15754          */
15755         maxWidth : 600,
15756         /**
15757          * The minimum width in pixels of the message box (defaults to 100)
15758          * @type Number
15759          */
15760         minWidth : 100,
15761         /**
15762          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15763          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15764          * @type Number
15765          */
15766         minProgressWidth : 250,
15767         /**
15768          * An object containing the default button text strings that can be overriden for localized language support.
15769          * Supported properties are: ok, cancel, yes and no.
15770          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15771          * @type Object
15772          */
15773         buttonText : {
15774             ok : "OK",
15775             cancel : "Cancel",
15776             yes : "Yes",
15777             no : "No"
15778         }
15779     };
15780 }();
15781
15782 /**
15783  * Shorthand for {@link Roo.MessageBox}
15784  */
15785 Roo.Msg = Roo.MessageBox;/*
15786  * Based on:
15787  * Ext JS Library 1.1.1
15788  * Copyright(c) 2006-2007, Ext JS, LLC.
15789  *
15790  * Originally Released Under LGPL - original licence link has changed is not relivant.
15791  *
15792  * Fork - LGPL
15793  * <script type="text/javascript">
15794  */
15795 /**
15796  * @class Roo.QuickTips
15797  * Provides attractive and customizable tooltips for any element.
15798  * @singleton
15799  */
15800 Roo.QuickTips = function(){
15801     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15802     var ce, bd, xy, dd;
15803     var visible = false, disabled = true, inited = false;
15804     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15805     
15806     var onOver = function(e){
15807         if(disabled){
15808             return;
15809         }
15810         var t = e.getTarget();
15811         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15812             return;
15813         }
15814         if(ce && t == ce.el){
15815             clearTimeout(hideProc);
15816             return;
15817         }
15818         if(t && tagEls[t.id]){
15819             tagEls[t.id].el = t;
15820             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15821             return;
15822         }
15823         var ttp, et = Roo.fly(t);
15824         var ns = cfg.namespace;
15825         if(tm.interceptTitles && t.title){
15826             ttp = t.title;
15827             t.qtip = ttp;
15828             t.removeAttribute("title");
15829             e.preventDefault();
15830         }else{
15831             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15832         }
15833         if(ttp){
15834             showProc = show.defer(tm.showDelay, tm, [{
15835                 el: t, 
15836                 text: ttp, 
15837                 width: et.getAttributeNS(ns, cfg.width),
15838                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15839                 title: et.getAttributeNS(ns, cfg.title),
15840                     cls: et.getAttributeNS(ns, cfg.cls)
15841             }]);
15842         }
15843     };
15844     
15845     var onOut = function(e){
15846         clearTimeout(showProc);
15847         var t = e.getTarget();
15848         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15849             hideProc = setTimeout(hide, tm.hideDelay);
15850         }
15851     };
15852     
15853     var onMove = function(e){
15854         if(disabled){
15855             return;
15856         }
15857         xy = e.getXY();
15858         xy[1] += 18;
15859         if(tm.trackMouse && ce){
15860             el.setXY(xy);
15861         }
15862     };
15863     
15864     var onDown = function(e){
15865         clearTimeout(showProc);
15866         clearTimeout(hideProc);
15867         if(!e.within(el)){
15868             if(tm.hideOnClick){
15869                 hide();
15870                 tm.disable();
15871                 tm.enable.defer(100, tm);
15872             }
15873         }
15874     };
15875     
15876     var getPad = function(){
15877         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15878     };
15879
15880     var show = function(o){
15881         if(disabled){
15882             return;
15883         }
15884         clearTimeout(dismissProc);
15885         ce = o;
15886         if(removeCls){ // in case manually hidden
15887             el.removeClass(removeCls);
15888             removeCls = null;
15889         }
15890         if(ce.cls){
15891             el.addClass(ce.cls);
15892             removeCls = ce.cls;
15893         }
15894         if(ce.title){
15895             tipTitle.update(ce.title);
15896             tipTitle.show();
15897         }else{
15898             tipTitle.update('');
15899             tipTitle.hide();
15900         }
15901         el.dom.style.width  = tm.maxWidth+'px';
15902         //tipBody.dom.style.width = '';
15903         tipBodyText.update(o.text);
15904         var p = getPad(), w = ce.width;
15905         if(!w){
15906             var td = tipBodyText.dom;
15907             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15908             if(aw > tm.maxWidth){
15909                 w = tm.maxWidth;
15910             }else if(aw < tm.minWidth){
15911                 w = tm.minWidth;
15912             }else{
15913                 w = aw;
15914             }
15915         }
15916         //tipBody.setWidth(w);
15917         el.setWidth(parseInt(w, 10) + p);
15918         if(ce.autoHide === false){
15919             close.setDisplayed(true);
15920             if(dd){
15921                 dd.unlock();
15922             }
15923         }else{
15924             close.setDisplayed(false);
15925             if(dd){
15926                 dd.lock();
15927             }
15928         }
15929         if(xy){
15930             el.avoidY = xy[1]-18;
15931             el.setXY(xy);
15932         }
15933         if(tm.animate){
15934             el.setOpacity(.1);
15935             el.setStyle("visibility", "visible");
15936             el.fadeIn({callback: afterShow});
15937         }else{
15938             afterShow();
15939         }
15940     };
15941     
15942     var afterShow = function(){
15943         if(ce){
15944             el.show();
15945             esc.enable();
15946             if(tm.autoDismiss && ce.autoHide !== false){
15947                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15948             }
15949         }
15950     };
15951     
15952     var hide = function(noanim){
15953         clearTimeout(dismissProc);
15954         clearTimeout(hideProc);
15955         ce = null;
15956         if(el.isVisible()){
15957             esc.disable();
15958             if(noanim !== true && tm.animate){
15959                 el.fadeOut({callback: afterHide});
15960             }else{
15961                 afterHide();
15962             } 
15963         }
15964     };
15965     
15966     var afterHide = function(){
15967         el.hide();
15968         if(removeCls){
15969             el.removeClass(removeCls);
15970             removeCls = null;
15971         }
15972     };
15973     
15974     return {
15975         /**
15976         * @cfg {Number} minWidth
15977         * The minimum width of the quick tip (defaults to 40)
15978         */
15979        minWidth : 40,
15980         /**
15981         * @cfg {Number} maxWidth
15982         * The maximum width of the quick tip (defaults to 300)
15983         */
15984        maxWidth : 300,
15985         /**
15986         * @cfg {Boolean} interceptTitles
15987         * True to automatically use the element's DOM title value if available (defaults to false)
15988         */
15989        interceptTitles : false,
15990         /**
15991         * @cfg {Boolean} trackMouse
15992         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15993         */
15994        trackMouse : false,
15995         /**
15996         * @cfg {Boolean} hideOnClick
15997         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15998         */
15999        hideOnClick : true,
16000         /**
16001         * @cfg {Number} showDelay
16002         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16003         */
16004        showDelay : 500,
16005         /**
16006         * @cfg {Number} hideDelay
16007         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16008         */
16009        hideDelay : 200,
16010         /**
16011         * @cfg {Boolean} autoHide
16012         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16013         * Used in conjunction with hideDelay.
16014         */
16015        autoHide : true,
16016         /**
16017         * @cfg {Boolean}
16018         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16019         * (defaults to true).  Used in conjunction with autoDismissDelay.
16020         */
16021        autoDismiss : true,
16022         /**
16023         * @cfg {Number}
16024         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16025         */
16026        autoDismissDelay : 5000,
16027        /**
16028         * @cfg {Boolean} animate
16029         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16030         */
16031        animate : false,
16032
16033        /**
16034         * @cfg {String} title
16035         * Title text to display (defaults to '').  This can be any valid HTML markup.
16036         */
16037         title: '',
16038        /**
16039         * @cfg {String} text
16040         * Body text to display (defaults to '').  This can be any valid HTML markup.
16041         */
16042         text : '',
16043        /**
16044         * @cfg {String} cls
16045         * A CSS class to apply to the base quick tip element (defaults to '').
16046         */
16047         cls : '',
16048        /**
16049         * @cfg {Number} width
16050         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16051         * minWidth or maxWidth.
16052         */
16053         width : null,
16054
16055     /**
16056      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16057      * or display QuickTips in a page.
16058      */
16059        init : function(){
16060           tm = Roo.QuickTips;
16061           cfg = tm.tagConfig;
16062           if(!inited){
16063               if(!Roo.isReady){ // allow calling of init() before onReady
16064                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16065                   return;
16066               }
16067               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16068               el.fxDefaults = {stopFx: true};
16069               // maximum custom styling
16070               //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>');
16071               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>');              
16072               tipTitle = el.child('h3');
16073               tipTitle.enableDisplayMode("block");
16074               tipBody = el.child('div.x-tip-bd');
16075               tipBodyText = el.child('div.x-tip-bd-inner');
16076               //bdLeft = el.child('div.x-tip-bd-left');
16077               //bdRight = el.child('div.x-tip-bd-right');
16078               close = el.child('div.x-tip-close');
16079               close.enableDisplayMode("block");
16080               close.on("click", hide);
16081               var d = Roo.get(document);
16082               d.on("mousedown", onDown);
16083               d.on("mouseover", onOver);
16084               d.on("mouseout", onOut);
16085               d.on("mousemove", onMove);
16086               esc = d.addKeyListener(27, hide);
16087               esc.disable();
16088               if(Roo.dd.DD){
16089                   dd = el.initDD("default", null, {
16090                       onDrag : function(){
16091                           el.sync();  
16092                       }
16093                   });
16094                   dd.setHandleElId(tipTitle.id);
16095                   dd.lock();
16096               }
16097               inited = true;
16098           }
16099           this.enable(); 
16100        },
16101
16102     /**
16103      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16104      * are supported:
16105      * <pre>
16106 Property    Type                   Description
16107 ----------  ---------------------  ------------------------------------------------------------------------
16108 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16109      * </ul>
16110      * @param {Object} config The config object
16111      */
16112        register : function(config){
16113            var cs = config instanceof Array ? config : arguments;
16114            for(var i = 0, len = cs.length; i < len; i++) {
16115                var c = cs[i];
16116                var target = c.target;
16117                if(target){
16118                    if(target instanceof Array){
16119                        for(var j = 0, jlen = target.length; j < jlen; j++){
16120                            tagEls[target[j]] = c;
16121                        }
16122                    }else{
16123                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16124                    }
16125                }
16126            }
16127        },
16128
16129     /**
16130      * Removes this quick tip from its element and destroys it.
16131      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16132      */
16133        unregister : function(el){
16134            delete tagEls[Roo.id(el)];
16135        },
16136
16137     /**
16138      * Enable this quick tip.
16139      */
16140        enable : function(){
16141            if(inited && disabled){
16142                locks.pop();
16143                if(locks.length < 1){
16144                    disabled = false;
16145                }
16146            }
16147        },
16148
16149     /**
16150      * Disable this quick tip.
16151      */
16152        disable : function(){
16153           disabled = true;
16154           clearTimeout(showProc);
16155           clearTimeout(hideProc);
16156           clearTimeout(dismissProc);
16157           if(ce){
16158               hide(true);
16159           }
16160           locks.push(1);
16161        },
16162
16163     /**
16164      * Returns true if the quick tip is enabled, else false.
16165      */
16166        isEnabled : function(){
16167             return !disabled;
16168        },
16169
16170         // private
16171        tagConfig : {
16172            namespace : "ext",
16173            attribute : "qtip",
16174            width : "width",
16175            target : "target",
16176            title : "qtitle",
16177            hide : "hide",
16178            cls : "qclass"
16179        }
16180    };
16181 }();
16182
16183 // backwards compat
16184 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16185  * Based on:
16186  * Ext JS Library 1.1.1
16187  * Copyright(c) 2006-2007, Ext JS, LLC.
16188  *
16189  * Originally Released Under LGPL - original licence link has changed is not relivant.
16190  *
16191  * Fork - LGPL
16192  * <script type="text/javascript">
16193  */
16194  
16195
16196 /**
16197  * @class Roo.tree.TreePanel
16198  * @extends Roo.data.Tree
16199
16200  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16201  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16202  * @cfg {Boolean} enableDD true to enable drag and drop
16203  * @cfg {Boolean} enableDrag true to enable just drag
16204  * @cfg {Boolean} enableDrop true to enable just drop
16205  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16206  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16207  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16208  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16209  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16210  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16211  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16212  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16213  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16214  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16215  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16216  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16217  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16218  * @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>
16219  * @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>
16220  * 
16221  * @constructor
16222  * @param {String/HTMLElement/Element} el The container element
16223  * @param {Object} config
16224  */
16225 Roo.tree.TreePanel = function(el, config){
16226     var root = false;
16227     var loader = false;
16228     if (config.root) {
16229         root = config.root;
16230         delete config.root;
16231     }
16232     if (config.loader) {
16233         loader = config.loader;
16234         delete config.loader;
16235     }
16236     
16237     Roo.apply(this, config);
16238     Roo.tree.TreePanel.superclass.constructor.call(this);
16239     this.el = Roo.get(el);
16240     this.el.addClass('x-tree');
16241     //console.log(root);
16242     if (root) {
16243         this.setRootNode( Roo.factory(root, Roo.tree));
16244     }
16245     if (loader) {
16246         this.loader = Roo.factory(loader, Roo.tree);
16247     }
16248    /**
16249     * Read-only. The id of the container element becomes this TreePanel's id.
16250     */
16251    this.id = this.el.id;
16252    this.addEvents({
16253         /**
16254         * @event beforeload
16255         * Fires before a node is loaded, return false to cancel
16256         * @param {Node} node The node being loaded
16257         */
16258         "beforeload" : true,
16259         /**
16260         * @event load
16261         * Fires when a node is loaded
16262         * @param {Node} node The node that was loaded
16263         */
16264         "load" : true,
16265         /**
16266         * @event textchange
16267         * Fires when the text for a node is changed
16268         * @param {Node} node The node
16269         * @param {String} text The new text
16270         * @param {String} oldText The old text
16271         */
16272         "textchange" : true,
16273         /**
16274         * @event beforeexpand
16275         * Fires before a node is expanded, return false to cancel.
16276         * @param {Node} node The node
16277         * @param {Boolean} deep
16278         * @param {Boolean} anim
16279         */
16280         "beforeexpand" : true,
16281         /**
16282         * @event beforecollapse
16283         * Fires before a node is collapsed, return false to cancel.
16284         * @param {Node} node The node
16285         * @param {Boolean} deep
16286         * @param {Boolean} anim
16287         */
16288         "beforecollapse" : true,
16289         /**
16290         * @event expand
16291         * Fires when a node is expanded
16292         * @param {Node} node The node
16293         */
16294         "expand" : true,
16295         /**
16296         * @event disabledchange
16297         * Fires when the disabled status of a node changes
16298         * @param {Node} node The node
16299         * @param {Boolean} disabled
16300         */
16301         "disabledchange" : true,
16302         /**
16303         * @event collapse
16304         * Fires when a node is collapsed
16305         * @param {Node} node The node
16306         */
16307         "collapse" : true,
16308         /**
16309         * @event beforeclick
16310         * Fires before click processing on a node. Return false to cancel the default action.
16311         * @param {Node} node The node
16312         * @param {Roo.EventObject} e The event object
16313         */
16314         "beforeclick":true,
16315         /**
16316         * @event checkchange
16317         * Fires when a node with a checkbox's checked property changes
16318         * @param {Node} this This node
16319         * @param {Boolean} checked
16320         */
16321         "checkchange":true,
16322         /**
16323         * @event click
16324         * Fires when a node is clicked
16325         * @param {Node} node The node
16326         * @param {Roo.EventObject} e The event object
16327         */
16328         "click":true,
16329         /**
16330         * @event dblclick
16331         * Fires when a node is double clicked
16332         * @param {Node} node The node
16333         * @param {Roo.EventObject} e The event object
16334         */
16335         "dblclick":true,
16336         /**
16337         * @event contextmenu
16338         * Fires when a node is right clicked
16339         * @param {Node} node The node
16340         * @param {Roo.EventObject} e The event object
16341         */
16342         "contextmenu":true,
16343         /**
16344         * @event beforechildrenrendered
16345         * Fires right before the child nodes for a node are rendered
16346         * @param {Node} node The node
16347         */
16348         "beforechildrenrendered":true,
16349        /**
16350              * @event startdrag
16351              * Fires when a node starts being dragged
16352              * @param {Roo.tree.TreePanel} this
16353              * @param {Roo.tree.TreeNode} node
16354              * @param {event} e The raw browser event
16355              */ 
16356             "startdrag" : true,
16357             /**
16358              * @event enddrag
16359              * Fires when a drag operation is complete
16360              * @param {Roo.tree.TreePanel} this
16361              * @param {Roo.tree.TreeNode} node
16362              * @param {event} e The raw browser event
16363              */
16364             "enddrag" : true,
16365             /**
16366              * @event dragdrop
16367              * Fires when a dragged node is dropped on a valid DD target
16368              * @param {Roo.tree.TreePanel} this
16369              * @param {Roo.tree.TreeNode} node
16370              * @param {DD} dd The dd it was dropped on
16371              * @param {event} e The raw browser event
16372              */
16373             "dragdrop" : true,
16374             /**
16375              * @event beforenodedrop
16376              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16377              * passed to handlers has the following properties:<br />
16378              * <ul style="padding:5px;padding-left:16px;">
16379              * <li>tree - The TreePanel</li>
16380              * <li>target - The node being targeted for the drop</li>
16381              * <li>data - The drag data from the drag source</li>
16382              * <li>point - The point of the drop - append, above or below</li>
16383              * <li>source - The drag source</li>
16384              * <li>rawEvent - Raw mouse event</li>
16385              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16386              * to be inserted by setting them on this object.</li>
16387              * <li>cancel - Set this to true to cancel the drop.</li>
16388              * </ul>
16389              * @param {Object} dropEvent
16390              */
16391             "beforenodedrop" : true,
16392             /**
16393              * @event nodedrop
16394              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16395              * passed to handlers has the following properties:<br />
16396              * <ul style="padding:5px;padding-left:16px;">
16397              * <li>tree - The TreePanel</li>
16398              * <li>target - The node being targeted for the drop</li>
16399              * <li>data - The drag data from the drag source</li>
16400              * <li>point - The point of the drop - append, above or below</li>
16401              * <li>source - The drag source</li>
16402              * <li>rawEvent - Raw mouse event</li>
16403              * <li>dropNode - Dropped node(s).</li>
16404              * </ul>
16405              * @param {Object} dropEvent
16406              */
16407             "nodedrop" : true,
16408              /**
16409              * @event nodedragover
16410              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16411              * passed to handlers has the following properties:<br />
16412              * <ul style="padding:5px;padding-left:16px;">
16413              * <li>tree - The TreePanel</li>
16414              * <li>target - The node being targeted for the drop</li>
16415              * <li>data - The drag data from the drag source</li>
16416              * <li>point - The point of the drop - append, above or below</li>
16417              * <li>source - The drag source</li>
16418              * <li>rawEvent - Raw mouse event</li>
16419              * <li>dropNode - Drop node(s) provided by the source.</li>
16420              * <li>cancel - Set this to true to signal drop not allowed.</li>
16421              * </ul>
16422              * @param {Object} dragOverEvent
16423              */
16424             "nodedragover" : true
16425         
16426    });
16427    if(this.singleExpand){
16428        this.on("beforeexpand", this.restrictExpand, this);
16429    }
16430 };
16431 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16432     rootVisible : true,
16433     animate: Roo.enableFx,
16434     lines : true,
16435     enableDD : false,
16436     hlDrop : Roo.enableFx,
16437   
16438     renderer: false,
16439     
16440     rendererTip: false,
16441     // private
16442     restrictExpand : function(node){
16443         var p = node.parentNode;
16444         if(p){
16445             if(p.expandedChild && p.expandedChild.parentNode == p){
16446                 p.expandedChild.collapse();
16447             }
16448             p.expandedChild = node;
16449         }
16450     },
16451
16452     // private override
16453     setRootNode : function(node){
16454         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16455         if(!this.rootVisible){
16456             node.ui = new Roo.tree.RootTreeNodeUI(node);
16457         }
16458         return node;
16459     },
16460
16461     /**
16462      * Returns the container element for this TreePanel
16463      */
16464     getEl : function(){
16465         return this.el;
16466     },
16467
16468     /**
16469      * Returns the default TreeLoader for this TreePanel
16470      */
16471     getLoader : function(){
16472         return this.loader;
16473     },
16474
16475     /**
16476      * Expand all nodes
16477      */
16478     expandAll : function(){
16479         this.root.expand(true);
16480     },
16481
16482     /**
16483      * Collapse all nodes
16484      */
16485     collapseAll : function(){
16486         this.root.collapse(true);
16487     },
16488
16489     /**
16490      * Returns the selection model used by this TreePanel
16491      */
16492     getSelectionModel : function(){
16493         if(!this.selModel){
16494             this.selModel = new Roo.tree.DefaultSelectionModel();
16495         }
16496         return this.selModel;
16497     },
16498
16499     /**
16500      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16501      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16502      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16503      * @return {Array}
16504      */
16505     getChecked : function(a, startNode){
16506         startNode = startNode || this.root;
16507         var r = [];
16508         var f = function(){
16509             if(this.attributes.checked){
16510                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16511             }
16512         }
16513         startNode.cascade(f);
16514         return r;
16515     },
16516
16517     /**
16518      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16519      * @param {String} path
16520      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16521      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16522      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16523      */
16524     expandPath : function(path, attr, callback){
16525         attr = attr || "id";
16526         var keys = path.split(this.pathSeparator);
16527         var curNode = this.root;
16528         if(curNode.attributes[attr] != keys[1]){ // invalid root
16529             if(callback){
16530                 callback(false, null);
16531             }
16532             return;
16533         }
16534         var index = 1;
16535         var f = function(){
16536             if(++index == keys.length){
16537                 if(callback){
16538                     callback(true, curNode);
16539                 }
16540                 return;
16541             }
16542             var c = curNode.findChild(attr, keys[index]);
16543             if(!c){
16544                 if(callback){
16545                     callback(false, curNode);
16546                 }
16547                 return;
16548             }
16549             curNode = c;
16550             c.expand(false, false, f);
16551         };
16552         curNode.expand(false, false, f);
16553     },
16554
16555     /**
16556      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16557      * @param {String} path
16558      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16559      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16560      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16561      */
16562     selectPath : function(path, attr, callback){
16563         attr = attr || "id";
16564         var keys = path.split(this.pathSeparator);
16565         var v = keys.pop();
16566         if(keys.length > 0){
16567             var f = function(success, node){
16568                 if(success && node){
16569                     var n = node.findChild(attr, v);
16570                     if(n){
16571                         n.select();
16572                         if(callback){
16573                             callback(true, n);
16574                         }
16575                     }else if(callback){
16576                         callback(false, n);
16577                     }
16578                 }else{
16579                     if(callback){
16580                         callback(false, n);
16581                     }
16582                 }
16583             };
16584             this.expandPath(keys.join(this.pathSeparator), attr, f);
16585         }else{
16586             this.root.select();
16587             if(callback){
16588                 callback(true, this.root);
16589             }
16590         }
16591     },
16592
16593     getTreeEl : function(){
16594         return this.el;
16595     },
16596
16597     /**
16598      * Trigger rendering of this TreePanel
16599      */
16600     render : function(){
16601         if (this.innerCt) {
16602             return this; // stop it rendering more than once!!
16603         }
16604         
16605         this.innerCt = this.el.createChild({tag:"ul",
16606                cls:"x-tree-root-ct " +
16607                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16608
16609         if(this.containerScroll){
16610             Roo.dd.ScrollManager.register(this.el);
16611         }
16612         if((this.enableDD || this.enableDrop) && !this.dropZone){
16613            /**
16614             * The dropZone used by this tree if drop is enabled
16615             * @type Roo.tree.TreeDropZone
16616             */
16617              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16618                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16619            });
16620         }
16621         if((this.enableDD || this.enableDrag) && !this.dragZone){
16622            /**
16623             * The dragZone used by this tree if drag is enabled
16624             * @type Roo.tree.TreeDragZone
16625             */
16626             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16627                ddGroup: this.ddGroup || "TreeDD",
16628                scroll: this.ddScroll
16629            });
16630         }
16631         this.getSelectionModel().init(this);
16632         if (!this.root) {
16633             console.log("ROOT not set in tree");
16634             return;
16635         }
16636         this.root.render();
16637         if(!this.rootVisible){
16638             this.root.renderChildren();
16639         }
16640         return this;
16641     }
16642 });/*
16643  * Based on:
16644  * Ext JS Library 1.1.1
16645  * Copyright(c) 2006-2007, Ext JS, LLC.
16646  *
16647  * Originally Released Under LGPL - original licence link has changed is not relivant.
16648  *
16649  * Fork - LGPL
16650  * <script type="text/javascript">
16651  */
16652  
16653
16654 /**
16655  * @class Roo.tree.DefaultSelectionModel
16656  * @extends Roo.util.Observable
16657  * The default single selection for a TreePanel.
16658  */
16659 Roo.tree.DefaultSelectionModel = function(){
16660    this.selNode = null;
16661    
16662    this.addEvents({
16663        /**
16664         * @event selectionchange
16665         * Fires when the selected node changes
16666         * @param {DefaultSelectionModel} this
16667         * @param {TreeNode} node the new selection
16668         */
16669        "selectionchange" : true,
16670
16671        /**
16672         * @event beforeselect
16673         * Fires before the selected node changes, return false to cancel the change
16674         * @param {DefaultSelectionModel} this
16675         * @param {TreeNode} node the new selection
16676         * @param {TreeNode} node the old selection
16677         */
16678        "beforeselect" : true
16679    });
16680 };
16681
16682 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16683     init : function(tree){
16684         this.tree = tree;
16685         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16686         tree.on("click", this.onNodeClick, this);
16687     },
16688     
16689     onNodeClick : function(node, e){
16690         if (e.ctrlKey && this.selNode == node)  {
16691             this.unselect(node);
16692             return;
16693         }
16694         this.select(node);
16695     },
16696     
16697     /**
16698      * Select a node.
16699      * @param {TreeNode} node The node to select
16700      * @return {TreeNode} The selected node
16701      */
16702     select : function(node){
16703         var last = this.selNode;
16704         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16705             if(last){
16706                 last.ui.onSelectedChange(false);
16707             }
16708             this.selNode = node;
16709             node.ui.onSelectedChange(true);
16710             this.fireEvent("selectionchange", this, node, last);
16711         }
16712         return node;
16713     },
16714     
16715     /**
16716      * Deselect a node.
16717      * @param {TreeNode} node The node to unselect
16718      */
16719     unselect : function(node){
16720         if(this.selNode == node){
16721             this.clearSelections();
16722         }    
16723     },
16724     
16725     /**
16726      * Clear all selections
16727      */
16728     clearSelections : function(){
16729         var n = this.selNode;
16730         if(n){
16731             n.ui.onSelectedChange(false);
16732             this.selNode = null;
16733             this.fireEvent("selectionchange", this, null);
16734         }
16735         return n;
16736     },
16737     
16738     /**
16739      * Get the selected node
16740      * @return {TreeNode} The selected node
16741      */
16742     getSelectedNode : function(){
16743         return this.selNode;    
16744     },
16745     
16746     /**
16747      * Returns true if the node is selected
16748      * @param {TreeNode} node The node to check
16749      * @return {Boolean}
16750      */
16751     isSelected : function(node){
16752         return this.selNode == node;  
16753     },
16754
16755     /**
16756      * Selects the node above the selected node in the tree, intelligently walking the nodes
16757      * @return TreeNode The new selection
16758      */
16759     selectPrevious : function(){
16760         var s = this.selNode || this.lastSelNode;
16761         if(!s){
16762             return null;
16763         }
16764         var ps = s.previousSibling;
16765         if(ps){
16766             if(!ps.isExpanded() || ps.childNodes.length < 1){
16767                 return this.select(ps);
16768             } else{
16769                 var lc = ps.lastChild;
16770                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16771                     lc = lc.lastChild;
16772                 }
16773                 return this.select(lc);
16774             }
16775         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16776             return this.select(s.parentNode);
16777         }
16778         return null;
16779     },
16780
16781     /**
16782      * Selects the node above the selected node in the tree, intelligently walking the nodes
16783      * @return TreeNode The new selection
16784      */
16785     selectNext : function(){
16786         var s = this.selNode || this.lastSelNode;
16787         if(!s){
16788             return null;
16789         }
16790         if(s.firstChild && s.isExpanded()){
16791              return this.select(s.firstChild);
16792          }else if(s.nextSibling){
16793              return this.select(s.nextSibling);
16794          }else if(s.parentNode){
16795             var newS = null;
16796             s.parentNode.bubble(function(){
16797                 if(this.nextSibling){
16798                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16799                     return false;
16800                 }
16801             });
16802             return newS;
16803          }
16804         return null;
16805     },
16806
16807     onKeyDown : function(e){
16808         var s = this.selNode || this.lastSelNode;
16809         // undesirable, but required
16810         var sm = this;
16811         if(!s){
16812             return;
16813         }
16814         var k = e.getKey();
16815         switch(k){
16816              case e.DOWN:
16817                  e.stopEvent();
16818                  this.selectNext();
16819              break;
16820              case e.UP:
16821                  e.stopEvent();
16822                  this.selectPrevious();
16823              break;
16824              case e.RIGHT:
16825                  e.preventDefault();
16826                  if(s.hasChildNodes()){
16827                      if(!s.isExpanded()){
16828                          s.expand();
16829                      }else if(s.firstChild){
16830                          this.select(s.firstChild, e);
16831                      }
16832                  }
16833              break;
16834              case e.LEFT:
16835                  e.preventDefault();
16836                  if(s.hasChildNodes() && s.isExpanded()){
16837                      s.collapse();
16838                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16839                      this.select(s.parentNode, e);
16840                  }
16841              break;
16842         };
16843     }
16844 });
16845
16846 /**
16847  * @class Roo.tree.MultiSelectionModel
16848  * @extends Roo.util.Observable
16849  * Multi selection for a TreePanel.
16850  */
16851 Roo.tree.MultiSelectionModel = function(){
16852    this.selNodes = [];
16853    this.selMap = {};
16854    this.addEvents({
16855        /**
16856         * @event selectionchange
16857         * Fires when the selected nodes change
16858         * @param {MultiSelectionModel} this
16859         * @param {Array} nodes Array of the selected nodes
16860         */
16861        "selectionchange" : true
16862    });
16863 };
16864
16865 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16866     init : function(tree){
16867         this.tree = tree;
16868         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16869         tree.on("click", this.onNodeClick, this);
16870     },
16871     
16872     onNodeClick : function(node, e){
16873         this.select(node, e, e.ctrlKey);
16874     },
16875     
16876     /**
16877      * Select a node.
16878      * @param {TreeNode} node The node to select
16879      * @param {EventObject} e (optional) An event associated with the selection
16880      * @param {Boolean} keepExisting True to retain existing selections
16881      * @return {TreeNode} The selected node
16882      */
16883     select : function(node, e, keepExisting){
16884         if(keepExisting !== true){
16885             this.clearSelections(true);
16886         }
16887         if(this.isSelected(node)){
16888             this.lastSelNode = node;
16889             return node;
16890         }
16891         this.selNodes.push(node);
16892         this.selMap[node.id] = node;
16893         this.lastSelNode = node;
16894         node.ui.onSelectedChange(true);
16895         this.fireEvent("selectionchange", this, this.selNodes);
16896         return node;
16897     },
16898     
16899     /**
16900      * Deselect a node.
16901      * @param {TreeNode} node The node to unselect
16902      */
16903     unselect : function(node){
16904         if(this.selMap[node.id]){
16905             node.ui.onSelectedChange(false);
16906             var sn = this.selNodes;
16907             var index = -1;
16908             if(sn.indexOf){
16909                 index = sn.indexOf(node);
16910             }else{
16911                 for(var i = 0, len = sn.length; i < len; i++){
16912                     if(sn[i] == node){
16913                         index = i;
16914                         break;
16915                     }
16916                 }
16917             }
16918             if(index != -1){
16919                 this.selNodes.splice(index, 1);
16920             }
16921             delete this.selMap[node.id];
16922             this.fireEvent("selectionchange", this, this.selNodes);
16923         }
16924     },
16925     
16926     /**
16927      * Clear all selections
16928      */
16929     clearSelections : function(suppressEvent){
16930         var sn = this.selNodes;
16931         if(sn.length > 0){
16932             for(var i = 0, len = sn.length; i < len; i++){
16933                 sn[i].ui.onSelectedChange(false);
16934             }
16935             this.selNodes = [];
16936             this.selMap = {};
16937             if(suppressEvent !== true){
16938                 this.fireEvent("selectionchange", this, this.selNodes);
16939             }
16940         }
16941     },
16942     
16943     /**
16944      * Returns true if the node is selected
16945      * @param {TreeNode} node The node to check
16946      * @return {Boolean}
16947      */
16948     isSelected : function(node){
16949         return this.selMap[node.id] ? true : false;  
16950     },
16951     
16952     /**
16953      * Returns an array of the selected nodes
16954      * @return {Array}
16955      */
16956     getSelectedNodes : function(){
16957         return this.selNodes;    
16958     },
16959
16960     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16961
16962     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16963
16964     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16965 });/*
16966  * Based on:
16967  * Ext JS Library 1.1.1
16968  * Copyright(c) 2006-2007, Ext JS, LLC.
16969  *
16970  * Originally Released Under LGPL - original licence link has changed is not relivant.
16971  *
16972  * Fork - LGPL
16973  * <script type="text/javascript">
16974  */
16975  
16976 /**
16977  * @class Roo.tree.TreeNode
16978  * @extends Roo.data.Node
16979  * @cfg {String} text The text for this node
16980  * @cfg {Boolean} expanded true to start the node expanded
16981  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16982  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16983  * @cfg {Boolean} disabled true to start the node disabled
16984  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16985  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16986  * @cfg {String} cls A css class to be added to the node
16987  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16988  * @cfg {String} href URL of the link used for the node (defaults to #)
16989  * @cfg {String} hrefTarget target frame for the link
16990  * @cfg {String} qtip An Ext QuickTip for the node
16991  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16992  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16993  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16994  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16995  * (defaults to undefined with no checkbox rendered)
16996  * @constructor
16997  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16998  */
16999 Roo.tree.TreeNode = function(attributes){
17000     attributes = attributes || {};
17001     if(typeof attributes == "string"){
17002         attributes = {text: attributes};
17003     }
17004     this.childrenRendered = false;
17005     this.rendered = false;
17006     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17007     this.expanded = attributes.expanded === true;
17008     this.isTarget = attributes.isTarget !== false;
17009     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17010     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17011
17012     /**
17013      * Read-only. The text for this node. To change it use setText().
17014      * @type String
17015      */
17016     this.text = attributes.text;
17017     /**
17018      * True if this node is disabled.
17019      * @type Boolean
17020      */
17021     this.disabled = attributes.disabled === true;
17022
17023     this.addEvents({
17024         /**
17025         * @event textchange
17026         * Fires when the text for this node is changed
17027         * @param {Node} this This node
17028         * @param {String} text The new text
17029         * @param {String} oldText The old text
17030         */
17031         "textchange" : true,
17032         /**
17033         * @event beforeexpand
17034         * Fires before this node is expanded, return false to cancel.
17035         * @param {Node} this This node
17036         * @param {Boolean} deep
17037         * @param {Boolean} anim
17038         */
17039         "beforeexpand" : true,
17040         /**
17041         * @event beforecollapse
17042         * Fires before this node is collapsed, return false to cancel.
17043         * @param {Node} this This node
17044         * @param {Boolean} deep
17045         * @param {Boolean} anim
17046         */
17047         "beforecollapse" : true,
17048         /**
17049         * @event expand
17050         * Fires when this node is expanded
17051         * @param {Node} this This node
17052         */
17053         "expand" : true,
17054         /**
17055         * @event disabledchange
17056         * Fires when the disabled status of this node changes
17057         * @param {Node} this This node
17058         * @param {Boolean} disabled
17059         */
17060         "disabledchange" : true,
17061         /**
17062         * @event collapse
17063         * Fires when this node is collapsed
17064         * @param {Node} this This node
17065         */
17066         "collapse" : true,
17067         /**
17068         * @event beforeclick
17069         * Fires before click processing. Return false to cancel the default action.
17070         * @param {Node} this This node
17071         * @param {Roo.EventObject} e The event object
17072         */
17073         "beforeclick":true,
17074         /**
17075         * @event checkchange
17076         * Fires when a node with a checkbox's checked property changes
17077         * @param {Node} this This node
17078         * @param {Boolean} checked
17079         */
17080         "checkchange":true,
17081         /**
17082         * @event click
17083         * Fires when this node is clicked
17084         * @param {Node} this This node
17085         * @param {Roo.EventObject} e The event object
17086         */
17087         "click":true,
17088         /**
17089         * @event dblclick
17090         * Fires when this node is double clicked
17091         * @param {Node} this This node
17092         * @param {Roo.EventObject} e The event object
17093         */
17094         "dblclick":true,
17095         /**
17096         * @event contextmenu
17097         * Fires when this node is right clicked
17098         * @param {Node} this This node
17099         * @param {Roo.EventObject} e The event object
17100         */
17101         "contextmenu":true,
17102         /**
17103         * @event beforechildrenrendered
17104         * Fires right before the child nodes for this node are rendered
17105         * @param {Node} this This node
17106         */
17107         "beforechildrenrendered":true
17108     });
17109
17110     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17111
17112     /**
17113      * Read-only. The UI for this node
17114      * @type TreeNodeUI
17115      */
17116     this.ui = new uiClass(this);
17117 };
17118 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17119     preventHScroll: true,
17120     /**
17121      * Returns true if this node is expanded
17122      * @return {Boolean}
17123      */
17124     isExpanded : function(){
17125         return this.expanded;
17126     },
17127
17128     /**
17129      * Returns the UI object for this node
17130      * @return {TreeNodeUI}
17131      */
17132     getUI : function(){
17133         return this.ui;
17134     },
17135
17136     // private override
17137     setFirstChild : function(node){
17138         var of = this.firstChild;
17139         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17140         if(this.childrenRendered && of && node != of){
17141             of.renderIndent(true, true);
17142         }
17143         if(this.rendered){
17144             this.renderIndent(true, true);
17145         }
17146     },
17147
17148     // private override
17149     setLastChild : function(node){
17150         var ol = this.lastChild;
17151         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17152         if(this.childrenRendered && ol && node != ol){
17153             ol.renderIndent(true, true);
17154         }
17155         if(this.rendered){
17156             this.renderIndent(true, true);
17157         }
17158     },
17159
17160     // these methods are overridden to provide lazy rendering support
17161     // private override
17162     appendChild : function(){
17163         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17164         if(node && this.childrenRendered){
17165             node.render();
17166         }
17167         this.ui.updateExpandIcon();
17168         return node;
17169     },
17170
17171     // private override
17172     removeChild : function(node){
17173         this.ownerTree.getSelectionModel().unselect(node);
17174         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17175         // if it's been rendered remove dom node
17176         if(this.childrenRendered){
17177             node.ui.remove();
17178         }
17179         if(this.childNodes.length < 1){
17180             this.collapse(false, false);
17181         }else{
17182             this.ui.updateExpandIcon();
17183         }
17184         if(!this.firstChild) {
17185             this.childrenRendered = false;
17186         }
17187         return node;
17188     },
17189
17190     // private override
17191     insertBefore : function(node, refNode){
17192         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17193         if(newNode && refNode && this.childrenRendered){
17194             node.render();
17195         }
17196         this.ui.updateExpandIcon();
17197         return newNode;
17198     },
17199
17200     /**
17201      * Sets the text for this node
17202      * @param {String} text
17203      */
17204     setText : function(text){
17205         var oldText = this.text;
17206         this.text = text;
17207         this.attributes.text = text;
17208         if(this.rendered){ // event without subscribing
17209             this.ui.onTextChange(this, text, oldText);
17210         }
17211         this.fireEvent("textchange", this, text, oldText);
17212     },
17213
17214     /**
17215      * Triggers selection of this node
17216      */
17217     select : function(){
17218         this.getOwnerTree().getSelectionModel().select(this);
17219     },
17220
17221     /**
17222      * Triggers deselection of this node
17223      */
17224     unselect : function(){
17225         this.getOwnerTree().getSelectionModel().unselect(this);
17226     },
17227
17228     /**
17229      * Returns true if this node is selected
17230      * @return {Boolean}
17231      */
17232     isSelected : function(){
17233         return this.getOwnerTree().getSelectionModel().isSelected(this);
17234     },
17235
17236     /**
17237      * Expand this node.
17238      * @param {Boolean} deep (optional) True to expand all children as well
17239      * @param {Boolean} anim (optional) false to cancel the default animation
17240      * @param {Function} callback (optional) A callback to be called when
17241      * expanding this node completes (does not wait for deep expand to complete).
17242      * Called with 1 parameter, this node.
17243      */
17244     expand : function(deep, anim, callback){
17245         if(!this.expanded){
17246             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17247                 return;
17248             }
17249             if(!this.childrenRendered){
17250                 this.renderChildren();
17251             }
17252             this.expanded = true;
17253             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17254                 this.ui.animExpand(function(){
17255                     this.fireEvent("expand", this);
17256                     if(typeof callback == "function"){
17257                         callback(this);
17258                     }
17259                     if(deep === true){
17260                         this.expandChildNodes(true);
17261                     }
17262                 }.createDelegate(this));
17263                 return;
17264             }else{
17265                 this.ui.expand();
17266                 this.fireEvent("expand", this);
17267                 if(typeof callback == "function"){
17268                     callback(this);
17269                 }
17270             }
17271         }else{
17272            if(typeof callback == "function"){
17273                callback(this);
17274            }
17275         }
17276         if(deep === true){
17277             this.expandChildNodes(true);
17278         }
17279     },
17280
17281     isHiddenRoot : function(){
17282         return this.isRoot && !this.getOwnerTree().rootVisible;
17283     },
17284
17285     /**
17286      * Collapse this node.
17287      * @param {Boolean} deep (optional) True to collapse all children as well
17288      * @param {Boolean} anim (optional) false to cancel the default animation
17289      */
17290     collapse : function(deep, anim){
17291         if(this.expanded && !this.isHiddenRoot()){
17292             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17293                 return;
17294             }
17295             this.expanded = false;
17296             if((this.getOwnerTree().animate && anim !== false) || anim){
17297                 this.ui.animCollapse(function(){
17298                     this.fireEvent("collapse", this);
17299                     if(deep === true){
17300                         this.collapseChildNodes(true);
17301                     }
17302                 }.createDelegate(this));
17303                 return;
17304             }else{
17305                 this.ui.collapse();
17306                 this.fireEvent("collapse", this);
17307             }
17308         }
17309         if(deep === true){
17310             var cs = this.childNodes;
17311             for(var i = 0, len = cs.length; i < len; i++) {
17312                 cs[i].collapse(true, false);
17313             }
17314         }
17315     },
17316
17317     // private
17318     delayedExpand : function(delay){
17319         if(!this.expandProcId){
17320             this.expandProcId = this.expand.defer(delay, this);
17321         }
17322     },
17323
17324     // private
17325     cancelExpand : function(){
17326         if(this.expandProcId){
17327             clearTimeout(this.expandProcId);
17328         }
17329         this.expandProcId = false;
17330     },
17331
17332     /**
17333      * Toggles expanded/collapsed state of the node
17334      */
17335     toggle : function(){
17336         if(this.expanded){
17337             this.collapse();
17338         }else{
17339             this.expand();
17340         }
17341     },
17342
17343     /**
17344      * Ensures all parent nodes are expanded
17345      */
17346     ensureVisible : function(callback){
17347         var tree = this.getOwnerTree();
17348         tree.expandPath(this.parentNode.getPath(), false, function(){
17349             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17350             Roo.callback(callback);
17351         }.createDelegate(this));
17352     },
17353
17354     /**
17355      * Expand all child nodes
17356      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17357      */
17358     expandChildNodes : function(deep){
17359         var cs = this.childNodes;
17360         for(var i = 0, len = cs.length; i < len; i++) {
17361                 cs[i].expand(deep);
17362         }
17363     },
17364
17365     /**
17366      * Collapse all child nodes
17367      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17368      */
17369     collapseChildNodes : function(deep){
17370         var cs = this.childNodes;
17371         for(var i = 0, len = cs.length; i < len; i++) {
17372                 cs[i].collapse(deep);
17373         }
17374     },
17375
17376     /**
17377      * Disables this node
17378      */
17379     disable : function(){
17380         this.disabled = true;
17381         this.unselect();
17382         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17383             this.ui.onDisableChange(this, true);
17384         }
17385         this.fireEvent("disabledchange", this, true);
17386     },
17387
17388     /**
17389      * Enables this node
17390      */
17391     enable : function(){
17392         this.disabled = false;
17393         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17394             this.ui.onDisableChange(this, false);
17395         }
17396         this.fireEvent("disabledchange", this, false);
17397     },
17398
17399     // private
17400     renderChildren : function(suppressEvent){
17401         if(suppressEvent !== false){
17402             this.fireEvent("beforechildrenrendered", this);
17403         }
17404         var cs = this.childNodes;
17405         for(var i = 0, len = cs.length; i < len; i++){
17406             cs[i].render(true);
17407         }
17408         this.childrenRendered = true;
17409     },
17410
17411     // private
17412     sort : function(fn, scope){
17413         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17414         if(this.childrenRendered){
17415             var cs = this.childNodes;
17416             for(var i = 0, len = cs.length; i < len; i++){
17417                 cs[i].render(true);
17418             }
17419         }
17420     },
17421
17422     // private
17423     render : function(bulkRender){
17424         this.ui.render(bulkRender);
17425         if(!this.rendered){
17426             this.rendered = true;
17427             if(this.expanded){
17428                 this.expanded = false;
17429                 this.expand(false, false);
17430             }
17431         }
17432     },
17433
17434     // private
17435     renderIndent : function(deep, refresh){
17436         if(refresh){
17437             this.ui.childIndent = null;
17438         }
17439         this.ui.renderIndent();
17440         if(deep === true && this.childrenRendered){
17441             var cs = this.childNodes;
17442             for(var i = 0, len = cs.length; i < len; i++){
17443                 cs[i].renderIndent(true, refresh);
17444             }
17445         }
17446     }
17447 });/*
17448  * Based on:
17449  * Ext JS Library 1.1.1
17450  * Copyright(c) 2006-2007, Ext JS, LLC.
17451  *
17452  * Originally Released Under LGPL - original licence link has changed is not relivant.
17453  *
17454  * Fork - LGPL
17455  * <script type="text/javascript">
17456  */
17457  
17458 /**
17459  * @class Roo.tree.AsyncTreeNode
17460  * @extends Roo.tree.TreeNode
17461  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17462  * @constructor
17463  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17464  */
17465  Roo.tree.AsyncTreeNode = function(config){
17466     this.loaded = false;
17467     this.loading = false;
17468     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17469     /**
17470     * @event beforeload
17471     * Fires before this node is loaded, return false to cancel
17472     * @param {Node} this This node
17473     */
17474     this.addEvents({'beforeload':true, 'load': true});
17475     /**
17476     * @event load
17477     * Fires when this node is loaded
17478     * @param {Node} this This node
17479     */
17480     /**
17481      * The loader used by this node (defaults to using the tree's defined loader)
17482      * @type TreeLoader
17483      * @property loader
17484      */
17485 };
17486 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17487     expand : function(deep, anim, callback){
17488         if(this.loading){ // if an async load is already running, waiting til it's done
17489             var timer;
17490             var f = function(){
17491                 if(!this.loading){ // done loading
17492                     clearInterval(timer);
17493                     this.expand(deep, anim, callback);
17494                 }
17495             }.createDelegate(this);
17496             timer = setInterval(f, 200);
17497             return;
17498         }
17499         if(!this.loaded){
17500             if(this.fireEvent("beforeload", this) === false){
17501                 return;
17502             }
17503             this.loading = true;
17504             this.ui.beforeLoad(this);
17505             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17506             if(loader){
17507                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17508                 return;
17509             }
17510         }
17511         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17512     },
17513     
17514     /**
17515      * Returns true if this node is currently loading
17516      * @return {Boolean}
17517      */
17518     isLoading : function(){
17519         return this.loading;  
17520     },
17521     
17522     loadComplete : function(deep, anim, callback){
17523         this.loading = false;
17524         this.loaded = true;
17525         this.ui.afterLoad(this);
17526         this.fireEvent("load", this);
17527         this.expand(deep, anim, callback);
17528     },
17529     
17530     /**
17531      * Returns true if this node has been loaded
17532      * @return {Boolean}
17533      */
17534     isLoaded : function(){
17535         return this.loaded;
17536     },
17537     
17538     hasChildNodes : function(){
17539         if(!this.isLeaf() && !this.loaded){
17540             return true;
17541         }else{
17542             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17543         }
17544     },
17545
17546     /**
17547      * Trigger a reload for this node
17548      * @param {Function} callback
17549      */
17550     reload : function(callback){
17551         this.collapse(false, false);
17552         while(this.firstChild){
17553             this.removeChild(this.firstChild);
17554         }
17555         this.childrenRendered = false;
17556         this.loaded = false;
17557         if(this.isHiddenRoot()){
17558             this.expanded = false;
17559         }
17560         this.expand(false, false, callback);
17561     }
17562 });/*
17563  * Based on:
17564  * Ext JS Library 1.1.1
17565  * Copyright(c) 2006-2007, Ext JS, LLC.
17566  *
17567  * Originally Released Under LGPL - original licence link has changed is not relivant.
17568  *
17569  * Fork - LGPL
17570  * <script type="text/javascript">
17571  */
17572  
17573 /**
17574  * @class Roo.tree.TreeNodeUI
17575  * @constructor
17576  * @param {Object} node The node to render
17577  * The TreeNode UI implementation is separate from the
17578  * tree implementation. Unless you are customizing the tree UI,
17579  * you should never have to use this directly.
17580  */
17581 Roo.tree.TreeNodeUI = function(node){
17582     this.node = node;
17583     this.rendered = false;
17584     this.animating = false;
17585     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17586 };
17587
17588 Roo.tree.TreeNodeUI.prototype = {
17589     removeChild : function(node){
17590         if(this.rendered){
17591             this.ctNode.removeChild(node.ui.getEl());
17592         }
17593     },
17594
17595     beforeLoad : function(){
17596          this.addClass("x-tree-node-loading");
17597     },
17598
17599     afterLoad : function(){
17600          this.removeClass("x-tree-node-loading");
17601     },
17602
17603     onTextChange : function(node, text, oldText){
17604         if(this.rendered){
17605             this.textNode.innerHTML = text;
17606         }
17607     },
17608
17609     onDisableChange : function(node, state){
17610         this.disabled = state;
17611         if(state){
17612             this.addClass("x-tree-node-disabled");
17613         }else{
17614             this.removeClass("x-tree-node-disabled");
17615         }
17616     },
17617
17618     onSelectedChange : function(state){
17619         if(state){
17620             this.focus();
17621             this.addClass("x-tree-selected");
17622         }else{
17623             //this.blur();
17624             this.removeClass("x-tree-selected");
17625         }
17626     },
17627
17628     onMove : function(tree, node, oldParent, newParent, index, refNode){
17629         this.childIndent = null;
17630         if(this.rendered){
17631             var targetNode = newParent.ui.getContainer();
17632             if(!targetNode){//target not rendered
17633                 this.holder = document.createElement("div");
17634                 this.holder.appendChild(this.wrap);
17635                 return;
17636             }
17637             var insertBefore = refNode ? refNode.ui.getEl() : null;
17638             if(insertBefore){
17639                 targetNode.insertBefore(this.wrap, insertBefore);
17640             }else{
17641                 targetNode.appendChild(this.wrap);
17642             }
17643             this.node.renderIndent(true);
17644         }
17645     },
17646
17647     addClass : function(cls){
17648         if(this.elNode){
17649             Roo.fly(this.elNode).addClass(cls);
17650         }
17651     },
17652
17653     removeClass : function(cls){
17654         if(this.elNode){
17655             Roo.fly(this.elNode).removeClass(cls);
17656         }
17657     },
17658
17659     remove : function(){
17660         if(this.rendered){
17661             this.holder = document.createElement("div");
17662             this.holder.appendChild(this.wrap);
17663         }
17664     },
17665
17666     fireEvent : function(){
17667         return this.node.fireEvent.apply(this.node, arguments);
17668     },
17669
17670     initEvents : function(){
17671         this.node.on("move", this.onMove, this);
17672         var E = Roo.EventManager;
17673         var a = this.anchor;
17674
17675         var el = Roo.fly(a, '_treeui');
17676
17677         if(Roo.isOpera){ // opera render bug ignores the CSS
17678             el.setStyle("text-decoration", "none");
17679         }
17680
17681         el.on("click", this.onClick, this);
17682         el.on("dblclick", this.onDblClick, this);
17683
17684         if(this.checkbox){
17685             Roo.EventManager.on(this.checkbox,
17686                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17687         }
17688
17689         el.on("contextmenu", this.onContextMenu, this);
17690
17691         var icon = Roo.fly(this.iconNode);
17692         icon.on("click", this.onClick, this);
17693         icon.on("dblclick", this.onDblClick, this);
17694         icon.on("contextmenu", this.onContextMenu, this);
17695         E.on(this.ecNode, "click", this.ecClick, this, true);
17696
17697         if(this.node.disabled){
17698             this.addClass("x-tree-node-disabled");
17699         }
17700         if(this.node.hidden){
17701             this.addClass("x-tree-node-disabled");
17702         }
17703         var ot = this.node.getOwnerTree();
17704         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17705         if(dd && (!this.node.isRoot || ot.rootVisible)){
17706             Roo.dd.Registry.register(this.elNode, {
17707                 node: this.node,
17708                 handles: this.getDDHandles(),
17709                 isHandle: false
17710             });
17711         }
17712     },
17713
17714     getDDHandles : function(){
17715         return [this.iconNode, this.textNode];
17716     },
17717
17718     hide : function(){
17719         if(this.rendered){
17720             this.wrap.style.display = "none";
17721         }
17722     },
17723
17724     show : function(){
17725         if(this.rendered){
17726             this.wrap.style.display = "";
17727         }
17728     },
17729
17730     onContextMenu : function(e){
17731         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17732             e.preventDefault();
17733             this.focus();
17734             this.fireEvent("contextmenu", this.node, e);
17735         }
17736     },
17737
17738     onClick : function(e){
17739         if(this.dropping){
17740             e.stopEvent();
17741             return;
17742         }
17743         if(this.fireEvent("beforeclick", this.node, e) !== false){
17744             if(!this.disabled && this.node.attributes.href){
17745                 this.fireEvent("click", this.node, e);
17746                 return;
17747             }
17748             e.preventDefault();
17749             if(this.disabled){
17750                 return;
17751             }
17752
17753             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17754                 this.node.toggle();
17755             }
17756
17757             this.fireEvent("click", this.node, e);
17758         }else{
17759             e.stopEvent();
17760         }
17761     },
17762
17763     onDblClick : function(e){
17764         e.preventDefault();
17765         if(this.disabled){
17766             return;
17767         }
17768         if(this.checkbox){
17769             this.toggleCheck();
17770         }
17771         if(!this.animating && this.node.hasChildNodes()){
17772             this.node.toggle();
17773         }
17774         this.fireEvent("dblclick", this.node, e);
17775     },
17776
17777     onCheckChange : function(){
17778         var checked = this.checkbox.checked;
17779         this.node.attributes.checked = checked;
17780         this.fireEvent('checkchange', this.node, checked);
17781     },
17782
17783     ecClick : function(e){
17784         if(!this.animating && this.node.hasChildNodes()){
17785             this.node.toggle();
17786         }
17787     },
17788
17789     startDrop : function(){
17790         this.dropping = true;
17791     },
17792
17793     // delayed drop so the click event doesn't get fired on a drop
17794     endDrop : function(){
17795        setTimeout(function(){
17796            this.dropping = false;
17797        }.createDelegate(this), 50);
17798     },
17799
17800     expand : function(){
17801         this.updateExpandIcon();
17802         this.ctNode.style.display = "";
17803     },
17804
17805     focus : function(){
17806         if(!this.node.preventHScroll){
17807             try{this.anchor.focus();
17808             }catch(e){}
17809         }else if(!Roo.isIE){
17810             try{
17811                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17812                 var l = noscroll.scrollLeft;
17813                 this.anchor.focus();
17814                 noscroll.scrollLeft = l;
17815             }catch(e){}
17816         }
17817     },
17818
17819     toggleCheck : function(value){
17820         var cb = this.checkbox;
17821         if(cb){
17822             cb.checked = (value === undefined ? !cb.checked : value);
17823         }
17824     },
17825
17826     blur : function(){
17827         try{
17828             this.anchor.blur();
17829         }catch(e){}
17830     },
17831
17832     animExpand : function(callback){
17833         var ct = Roo.get(this.ctNode);
17834         ct.stopFx();
17835         if(!this.node.hasChildNodes()){
17836             this.updateExpandIcon();
17837             this.ctNode.style.display = "";
17838             Roo.callback(callback);
17839             return;
17840         }
17841         this.animating = true;
17842         this.updateExpandIcon();
17843
17844         ct.slideIn('t', {
17845            callback : function(){
17846                this.animating = false;
17847                Roo.callback(callback);
17848             },
17849             scope: this,
17850             duration: this.node.ownerTree.duration || .25
17851         });
17852     },
17853
17854     highlight : function(){
17855         var tree = this.node.getOwnerTree();
17856         Roo.fly(this.wrap).highlight(
17857             tree.hlColor || "C3DAF9",
17858             {endColor: tree.hlBaseColor}
17859         );
17860     },
17861
17862     collapse : function(){
17863         this.updateExpandIcon();
17864         this.ctNode.style.display = "none";
17865     },
17866
17867     animCollapse : function(callback){
17868         var ct = Roo.get(this.ctNode);
17869         ct.enableDisplayMode('block');
17870         ct.stopFx();
17871
17872         this.animating = true;
17873         this.updateExpandIcon();
17874
17875         ct.slideOut('t', {
17876             callback : function(){
17877                this.animating = false;
17878                Roo.callback(callback);
17879             },
17880             scope: this,
17881             duration: this.node.ownerTree.duration || .25
17882         });
17883     },
17884
17885     getContainer : function(){
17886         return this.ctNode;
17887     },
17888
17889     getEl : function(){
17890         return this.wrap;
17891     },
17892
17893     appendDDGhost : function(ghostNode){
17894         ghostNode.appendChild(this.elNode.cloneNode(true));
17895     },
17896
17897     getDDRepairXY : function(){
17898         return Roo.lib.Dom.getXY(this.iconNode);
17899     },
17900
17901     onRender : function(){
17902         this.render();
17903     },
17904
17905     render : function(bulkRender){
17906         var n = this.node, a = n.attributes;
17907         var targetNode = n.parentNode ?
17908               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17909
17910         if(!this.rendered){
17911             this.rendered = true;
17912
17913             this.renderElements(n, a, targetNode, bulkRender);
17914
17915             if(a.qtip){
17916                if(this.textNode.setAttributeNS){
17917                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17918                    if(a.qtipTitle){
17919                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17920                    }
17921                }else{
17922                    this.textNode.setAttribute("ext:qtip", a.qtip);
17923                    if(a.qtipTitle){
17924                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17925                    }
17926                }
17927             }else if(a.qtipCfg){
17928                 a.qtipCfg.target = Roo.id(this.textNode);
17929                 Roo.QuickTips.register(a.qtipCfg);
17930             }
17931             this.initEvents();
17932             if(!this.node.expanded){
17933                 this.updateExpandIcon();
17934             }
17935         }else{
17936             if(bulkRender === true) {
17937                 targetNode.appendChild(this.wrap);
17938             }
17939         }
17940     },
17941
17942     renderElements : function(n, a, targetNode, bulkRender){
17943         // add some indent caching, this helps performance when rendering a large tree
17944         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17945         var t = n.getOwnerTree();
17946         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17947         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17948         var cb = typeof a.checked == 'boolean';
17949         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17950         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17951             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17952             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17953             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17954             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17955             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17956              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17957                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17958             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17959             "</li>"];
17960
17961         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17962             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17963                                 n.nextSibling.ui.getEl(), buf.join(""));
17964         }else{
17965             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17966         }
17967
17968         this.elNode = this.wrap.childNodes[0];
17969         this.ctNode = this.wrap.childNodes[1];
17970         var cs = this.elNode.childNodes;
17971         this.indentNode = cs[0];
17972         this.ecNode = cs[1];
17973         this.iconNode = cs[2];
17974         var index = 3;
17975         if(cb){
17976             this.checkbox = cs[3];
17977             index++;
17978         }
17979         this.anchor = cs[index];
17980         this.textNode = cs[index].firstChild;
17981     },
17982
17983     getAnchor : function(){
17984         return this.anchor;
17985     },
17986
17987     getTextEl : function(){
17988         return this.textNode;
17989     },
17990
17991     getIconEl : function(){
17992         return this.iconNode;
17993     },
17994
17995     isChecked : function(){
17996         return this.checkbox ? this.checkbox.checked : false;
17997     },
17998
17999     updateExpandIcon : function(){
18000         if(this.rendered){
18001             var n = this.node, c1, c2;
18002             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18003             var hasChild = n.hasChildNodes();
18004             if(hasChild){
18005                 if(n.expanded){
18006                     cls += "-minus";
18007                     c1 = "x-tree-node-collapsed";
18008                     c2 = "x-tree-node-expanded";
18009                 }else{
18010                     cls += "-plus";
18011                     c1 = "x-tree-node-expanded";
18012                     c2 = "x-tree-node-collapsed";
18013                 }
18014                 if(this.wasLeaf){
18015                     this.removeClass("x-tree-node-leaf");
18016                     this.wasLeaf = false;
18017                 }
18018                 if(this.c1 != c1 || this.c2 != c2){
18019                     Roo.fly(this.elNode).replaceClass(c1, c2);
18020                     this.c1 = c1; this.c2 = c2;
18021                 }
18022             }else{
18023                 if(!this.wasLeaf){
18024                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18025                     delete this.c1;
18026                     delete this.c2;
18027                     this.wasLeaf = true;
18028                 }
18029             }
18030             var ecc = "x-tree-ec-icon "+cls;
18031             if(this.ecc != ecc){
18032                 this.ecNode.className = ecc;
18033                 this.ecc = ecc;
18034             }
18035         }
18036     },
18037
18038     getChildIndent : function(){
18039         if(!this.childIndent){
18040             var buf = [];
18041             var p = this.node;
18042             while(p){
18043                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18044                     if(!p.isLast()) {
18045                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18046                     } else {
18047                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18048                     }
18049                 }
18050                 p = p.parentNode;
18051             }
18052             this.childIndent = buf.join("");
18053         }
18054         return this.childIndent;
18055     },
18056
18057     renderIndent : function(){
18058         if(this.rendered){
18059             var indent = "";
18060             var p = this.node.parentNode;
18061             if(p){
18062                 indent = p.ui.getChildIndent();
18063             }
18064             if(this.indentMarkup != indent){ // don't rerender if not required
18065                 this.indentNode.innerHTML = indent;
18066                 this.indentMarkup = indent;
18067             }
18068             this.updateExpandIcon();
18069         }
18070     }
18071 };
18072
18073 Roo.tree.RootTreeNodeUI = function(){
18074     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18075 };
18076 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18077     render : function(){
18078         if(!this.rendered){
18079             var targetNode = this.node.ownerTree.innerCt.dom;
18080             this.node.expanded = true;
18081             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18082             this.wrap = this.ctNode = targetNode.firstChild;
18083         }
18084     },
18085     collapse : function(){
18086     },
18087     expand : function(){
18088     }
18089 });/*
18090  * Based on:
18091  * Ext JS Library 1.1.1
18092  * Copyright(c) 2006-2007, Ext JS, LLC.
18093  *
18094  * Originally Released Under LGPL - original licence link has changed is not relivant.
18095  *
18096  * Fork - LGPL
18097  * <script type="text/javascript">
18098  */
18099 /**
18100  * @class Roo.tree.TreeLoader
18101  * @extends Roo.util.Observable
18102  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18103  * nodes from a specified URL. The response must be a javascript Array definition
18104  * who's elements are node definition objects. eg:
18105  * <pre><code>
18106    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18107     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18108 </code></pre>
18109  * <br><br>
18110  * A server request is sent, and child nodes are loaded only when a node is expanded.
18111  * The loading node's id is passed to the server under the parameter name "node" to
18112  * enable the server to produce the correct child nodes.
18113  * <br><br>
18114  * To pass extra parameters, an event handler may be attached to the "beforeload"
18115  * event, and the parameters specified in the TreeLoader's baseParams property:
18116  * <pre><code>
18117     myTreeLoader.on("beforeload", function(treeLoader, node) {
18118         this.baseParams.category = node.attributes.category;
18119     }, this);
18120 </code></pre><
18121  * This would pass an HTTP parameter called "category" to the server containing
18122  * the value of the Node's "category" attribute.
18123  * @constructor
18124  * Creates a new Treeloader.
18125  * @param {Object} config A config object containing config properties.
18126  */
18127 Roo.tree.TreeLoader = function(config){
18128     this.baseParams = {};
18129     this.requestMethod = "POST";
18130     Roo.apply(this, config);
18131
18132     this.addEvents({
18133     
18134         /**
18135          * @event beforeload
18136          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18137          * @param {Object} This TreeLoader object.
18138          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18139          * @param {Object} callback The callback function specified in the {@link #load} call.
18140          */
18141         beforeload : true,
18142         /**
18143          * @event load
18144          * Fires when the node has been successfuly loaded.
18145          * @param {Object} This TreeLoader object.
18146          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18147          * @param {Object} response The response object containing the data from the server.
18148          */
18149         load : true,
18150         /**
18151          * @event loadexception
18152          * Fires if the network request failed.
18153          * @param {Object} This TreeLoader object.
18154          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18155          * @param {Object} response The response object containing the data from the server.
18156          */
18157         loadexception : true,
18158         /**
18159          * @event create
18160          * Fires before a node is created, enabling you to return custom Node types 
18161          * @param {Object} This TreeLoader object.
18162          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18163          */
18164         create : true
18165     });
18166
18167     Roo.tree.TreeLoader.superclass.constructor.call(this);
18168 };
18169
18170 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18171     /**
18172     * @cfg {String} dataUrl The URL from which to request a Json string which
18173     * specifies an array of node definition object representing the child nodes
18174     * to be loaded.
18175     */
18176     /**
18177     * @cfg {Object} baseParams (optional) An object containing properties which
18178     * specify HTTP parameters to be passed to each request for child nodes.
18179     */
18180     /**
18181     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18182     * created by this loader. If the attributes sent by the server have an attribute in this object,
18183     * they take priority.
18184     */
18185     /**
18186     * @cfg {Object} uiProviders (optional) An object containing properties which
18187     * 
18188     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18189     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18190     * <i>uiProvider</i> attribute of a returned child node is a string rather
18191     * than a reference to a TreeNodeUI implementation, this that string value
18192     * is used as a property name in the uiProviders object. You can define the provider named
18193     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18194     */
18195     uiProviders : {},
18196
18197     /**
18198     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18199     * child nodes before loading.
18200     */
18201     clearOnLoad : true,
18202
18203     /**
18204     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18205     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18206     * Grid query { data : [ .....] }
18207     */
18208     
18209     root : false,
18210      /**
18211     * @cfg {String} queryParam (optional) 
18212     * Name of the query as it will be passed on the querystring (defaults to 'node')
18213     * eg. the request will be ?node=[id]
18214     */
18215     
18216     
18217     queryParam: false,
18218     
18219     /**
18220      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18221      * This is called automatically when a node is expanded, but may be used to reload
18222      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18223      * @param {Roo.tree.TreeNode} node
18224      * @param {Function} callback
18225      */
18226     load : function(node, callback){
18227         if(this.clearOnLoad){
18228             while(node.firstChild){
18229                 node.removeChild(node.firstChild);
18230             }
18231         }
18232         if(node.attributes.children){ // preloaded json children
18233             var cs = node.attributes.children;
18234             for(var i = 0, len = cs.length; i < len; i++){
18235                 node.appendChild(this.createNode(cs[i]));
18236             }
18237             if(typeof callback == "function"){
18238                 callback();
18239             }
18240         }else if(this.dataUrl){
18241             this.requestData(node, callback);
18242         }
18243     },
18244
18245     getParams: function(node){
18246         var buf = [], bp = this.baseParams;
18247         for(var key in bp){
18248             if(typeof bp[key] != "function"){
18249                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18250             }
18251         }
18252         var n = this.queryParam === false ? 'node' : this.queryParam;
18253         buf.push(n + "=", encodeURIComponent(node.id));
18254         return buf.join("");
18255     },
18256
18257     requestData : function(node, callback){
18258         if(this.fireEvent("beforeload", this, node, callback) !== false){
18259             this.transId = Roo.Ajax.request({
18260                 method:this.requestMethod,
18261                 url: this.dataUrl||this.url,
18262                 success: this.handleResponse,
18263                 failure: this.handleFailure,
18264                 scope: this,
18265                 argument: {callback: callback, node: node},
18266                 params: this.getParams(node)
18267             });
18268         }else{
18269             // if the load is cancelled, make sure we notify
18270             // the node that we are done
18271             if(typeof callback == "function"){
18272                 callback();
18273             }
18274         }
18275     },
18276
18277     isLoading : function(){
18278         return this.transId ? true : false;
18279     },
18280
18281     abort : function(){
18282         if(this.isLoading()){
18283             Roo.Ajax.abort(this.transId);
18284         }
18285     },
18286
18287     // private
18288     createNode : function(attr){
18289         // apply baseAttrs, nice idea Corey!
18290         if(this.baseAttrs){
18291             Roo.applyIf(attr, this.baseAttrs);
18292         }
18293         if(this.applyLoader !== false){
18294             attr.loader = this;
18295         }
18296         // uiProvider = depreciated..
18297         
18298         if(typeof(attr.uiProvider) == 'string'){
18299            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18300                 /**  eval:var:attr */ eval(attr.uiProvider);
18301         }
18302         if(typeof(this.uiProviders['default']) != 'undefined') {
18303             attr.uiProvider = this.uiProviders['default'];
18304         }
18305         
18306         this.fireEvent('create', this, attr);
18307         
18308         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18309         return(attr.leaf ?
18310                         new Roo.tree.TreeNode(attr) :
18311                         new Roo.tree.AsyncTreeNode(attr));
18312     },
18313
18314     processResponse : function(response, node, callback){
18315         var json = response.responseText;
18316         try {
18317             
18318             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18319             if (this.root !== false) {
18320                 o = o[this.root];
18321             }
18322             
18323             for(var i = 0, len = o.length; i < len; i++){
18324                 var n = this.createNode(o[i]);
18325                 if(n){
18326                     node.appendChild(n);
18327                 }
18328             }
18329             if(typeof callback == "function"){
18330                 callback(this, node);
18331             }
18332         }catch(e){
18333             this.handleFailure(response);
18334         }
18335     },
18336
18337     handleResponse : function(response){
18338         this.transId = false;
18339         var a = response.argument;
18340         this.processResponse(response, a.node, a.callback);
18341         this.fireEvent("load", this, a.node, response);
18342     },
18343
18344     handleFailure : function(response){
18345         this.transId = false;
18346         var a = response.argument;
18347         this.fireEvent("loadexception", this, a.node, response);
18348         if(typeof a.callback == "function"){
18349             a.callback(this, a.node);
18350         }
18351     }
18352 });/*
18353  * Based on:
18354  * Ext JS Library 1.1.1
18355  * Copyright(c) 2006-2007, Ext JS, LLC.
18356  *
18357  * Originally Released Under LGPL - original licence link has changed is not relivant.
18358  *
18359  * Fork - LGPL
18360  * <script type="text/javascript">
18361  */
18362
18363 /**
18364 * @class Roo.tree.TreeFilter
18365 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18366 * @param {TreePanel} tree
18367 * @param {Object} config (optional)
18368  */
18369 Roo.tree.TreeFilter = function(tree, config){
18370     this.tree = tree;
18371     this.filtered = {};
18372     Roo.apply(this, config);
18373 };
18374
18375 Roo.tree.TreeFilter.prototype = {
18376     clearBlank:false,
18377     reverse:false,
18378     autoClear:false,
18379     remove:false,
18380
18381      /**
18382      * Filter the data by a specific attribute.
18383      * @param {String/RegExp} value Either string that the attribute value
18384      * should start with or a RegExp to test against the attribute
18385      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18386      * @param {TreeNode} startNode (optional) The node to start the filter at.
18387      */
18388     filter : function(value, attr, startNode){
18389         attr = attr || "text";
18390         var f;
18391         if(typeof value == "string"){
18392             var vlen = value.length;
18393             // auto clear empty filter
18394             if(vlen == 0 && this.clearBlank){
18395                 this.clear();
18396                 return;
18397             }
18398             value = value.toLowerCase();
18399             f = function(n){
18400                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18401             };
18402         }else if(value.exec){ // regex?
18403             f = function(n){
18404                 return value.test(n.attributes[attr]);
18405             };
18406         }else{
18407             throw 'Illegal filter type, must be string or regex';
18408         }
18409         this.filterBy(f, null, startNode);
18410         },
18411
18412     /**
18413      * Filter by a function. The passed function will be called with each
18414      * node in the tree (or from the startNode). If the function returns true, the node is kept
18415      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18416      * @param {Function} fn The filter function
18417      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18418      */
18419     filterBy : function(fn, scope, startNode){
18420         startNode = startNode || this.tree.root;
18421         if(this.autoClear){
18422             this.clear();
18423         }
18424         var af = this.filtered, rv = this.reverse;
18425         var f = function(n){
18426             if(n == startNode){
18427                 return true;
18428             }
18429             if(af[n.id]){
18430                 return false;
18431             }
18432             var m = fn.call(scope || n, n);
18433             if(!m || rv){
18434                 af[n.id] = n;
18435                 n.ui.hide();
18436                 return false;
18437             }
18438             return true;
18439         };
18440         startNode.cascade(f);
18441         if(this.remove){
18442            for(var id in af){
18443                if(typeof id != "function"){
18444                    var n = af[id];
18445                    if(n && n.parentNode){
18446                        n.parentNode.removeChild(n);
18447                    }
18448                }
18449            }
18450         }
18451     },
18452
18453     /**
18454      * Clears the current filter. Note: with the "remove" option
18455      * set a filter cannot be cleared.
18456      */
18457     clear : function(){
18458         var t = this.tree;
18459         var af = this.filtered;
18460         for(var id in af){
18461             if(typeof id != "function"){
18462                 var n = af[id];
18463                 if(n){
18464                     n.ui.show();
18465                 }
18466             }
18467         }
18468         this.filtered = {};
18469     }
18470 };
18471 /*
18472  * Based on:
18473  * Ext JS Library 1.1.1
18474  * Copyright(c) 2006-2007, Ext JS, LLC.
18475  *
18476  * Originally Released Under LGPL - original licence link has changed is not relivant.
18477  *
18478  * Fork - LGPL
18479  * <script type="text/javascript">
18480  */
18481  
18482
18483 /**
18484  * @class Roo.tree.TreeSorter
18485  * Provides sorting of nodes in a TreePanel
18486  * 
18487  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18488  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18489  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18490  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18491  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18492  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18493  * @constructor
18494  * @param {TreePanel} tree
18495  * @param {Object} config
18496  */
18497 Roo.tree.TreeSorter = function(tree, config){
18498     Roo.apply(this, config);
18499     tree.on("beforechildrenrendered", this.doSort, this);
18500     tree.on("append", this.updateSort, this);
18501     tree.on("insert", this.updateSort, this);
18502     
18503     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18504     var p = this.property || "text";
18505     var sortType = this.sortType;
18506     var fs = this.folderSort;
18507     var cs = this.caseSensitive === true;
18508     var leafAttr = this.leafAttr || 'leaf';
18509
18510     this.sortFn = function(n1, n2){
18511         if(fs){
18512             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18513                 return 1;
18514             }
18515             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18516                 return -1;
18517             }
18518         }
18519         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18520         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18521         if(v1 < v2){
18522                         return dsc ? +1 : -1;
18523                 }else if(v1 > v2){
18524                         return dsc ? -1 : +1;
18525         }else{
18526                 return 0;
18527         }
18528     };
18529 };
18530
18531 Roo.tree.TreeSorter.prototype = {
18532     doSort : function(node){
18533         node.sort(this.sortFn);
18534     },
18535     
18536     compareNodes : function(n1, n2){
18537         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18538     },
18539     
18540     updateSort : function(tree, node){
18541         if(node.childrenRendered){
18542             this.doSort.defer(1, this, [node]);
18543         }
18544     }
18545 };/*
18546  * Based on:
18547  * Ext JS Library 1.1.1
18548  * Copyright(c) 2006-2007, Ext JS, LLC.
18549  *
18550  * Originally Released Under LGPL - original licence link has changed is not relivant.
18551  *
18552  * Fork - LGPL
18553  * <script type="text/javascript">
18554  */
18555
18556 if(Roo.dd.DropZone){
18557     
18558 Roo.tree.TreeDropZone = function(tree, config){
18559     this.allowParentInsert = false;
18560     this.allowContainerDrop = false;
18561     this.appendOnly = false;
18562     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18563     this.tree = tree;
18564     this.lastInsertClass = "x-tree-no-status";
18565     this.dragOverData = {};
18566 };
18567
18568 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18569     ddGroup : "TreeDD",
18570     
18571     expandDelay : 1000,
18572     
18573     expandNode : function(node){
18574         if(node.hasChildNodes() && !node.isExpanded()){
18575             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18576         }
18577     },
18578     
18579     queueExpand : function(node){
18580         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18581     },
18582     
18583     cancelExpand : function(){
18584         if(this.expandProcId){
18585             clearTimeout(this.expandProcId);
18586             this.expandProcId = false;
18587         }
18588     },
18589     
18590     isValidDropPoint : function(n, pt, dd, e, data){
18591         if(!n || !data){ return false; }
18592         var targetNode = n.node;
18593         var dropNode = data.node;
18594         // default drop rules
18595         if(!(targetNode && targetNode.isTarget && pt)){
18596             return false;
18597         }
18598         if(pt == "append" && targetNode.allowChildren === false){
18599             return false;
18600         }
18601         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18602             return false;
18603         }
18604         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18605             return false;
18606         }
18607         // reuse the object
18608         var overEvent = this.dragOverData;
18609         overEvent.tree = this.tree;
18610         overEvent.target = targetNode;
18611         overEvent.data = data;
18612         overEvent.point = pt;
18613         overEvent.source = dd;
18614         overEvent.rawEvent = e;
18615         overEvent.dropNode = dropNode;
18616         overEvent.cancel = false;  
18617         var result = this.tree.fireEvent("nodedragover", overEvent);
18618         return overEvent.cancel === false && result !== false;
18619     },
18620     
18621     getDropPoint : function(e, n, dd){
18622         var tn = n.node;
18623         if(tn.isRoot){
18624             return tn.allowChildren !== false ? "append" : false; // always append for root
18625         }
18626         var dragEl = n.ddel;
18627         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18628         var y = Roo.lib.Event.getPageY(e);
18629         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18630         
18631         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18632         var noAppend = tn.allowChildren === false;
18633         if(this.appendOnly || tn.parentNode.allowChildren === false){
18634             return noAppend ? false : "append";
18635         }
18636         var noBelow = false;
18637         if(!this.allowParentInsert){
18638             noBelow = tn.hasChildNodes() && tn.isExpanded();
18639         }
18640         var q = (b - t) / (noAppend ? 2 : 3);
18641         if(y >= t && y < (t + q)){
18642             return "above";
18643         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18644             return "below";
18645         }else{
18646             return "append";
18647         }
18648     },
18649     
18650     onNodeEnter : function(n, dd, e, data){
18651         this.cancelExpand();
18652     },
18653     
18654     onNodeOver : function(n, dd, e, data){
18655         var pt = this.getDropPoint(e, n, dd);
18656         var node = n.node;
18657         
18658         // auto node expand check
18659         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18660             this.queueExpand(node);
18661         }else if(pt != "append"){
18662             this.cancelExpand();
18663         }
18664         
18665         // set the insert point style on the target node
18666         var returnCls = this.dropNotAllowed;
18667         if(this.isValidDropPoint(n, pt, dd, e, data)){
18668            if(pt){
18669                var el = n.ddel;
18670                var cls;
18671                if(pt == "above"){
18672                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18673                    cls = "x-tree-drag-insert-above";
18674                }else if(pt == "below"){
18675                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18676                    cls = "x-tree-drag-insert-below";
18677                }else{
18678                    returnCls = "x-tree-drop-ok-append";
18679                    cls = "x-tree-drag-append";
18680                }
18681                if(this.lastInsertClass != cls){
18682                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18683                    this.lastInsertClass = cls;
18684                }
18685            }
18686        }
18687        return returnCls;
18688     },
18689     
18690     onNodeOut : function(n, dd, e, data){
18691         this.cancelExpand();
18692         this.removeDropIndicators(n);
18693     },
18694     
18695     onNodeDrop : function(n, dd, e, data){
18696         var point = this.getDropPoint(e, n, dd);
18697         var targetNode = n.node;
18698         targetNode.ui.startDrop();
18699         if(!this.isValidDropPoint(n, point, dd, e, data)){
18700             targetNode.ui.endDrop();
18701             return false;
18702         }
18703         // first try to find the drop node
18704         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18705         var dropEvent = {
18706             tree : this.tree,
18707             target: targetNode,
18708             data: data,
18709             point: point,
18710             source: dd,
18711             rawEvent: e,
18712             dropNode: dropNode,
18713             cancel: !dropNode   
18714         };
18715         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18716         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18717             targetNode.ui.endDrop();
18718             return false;
18719         }
18720         // allow target changing
18721         targetNode = dropEvent.target;
18722         if(point == "append" && !targetNode.isExpanded()){
18723             targetNode.expand(false, null, function(){
18724                 this.completeDrop(dropEvent);
18725             }.createDelegate(this));
18726         }else{
18727             this.completeDrop(dropEvent);
18728         }
18729         return true;
18730     },
18731     
18732     completeDrop : function(de){
18733         var ns = de.dropNode, p = de.point, t = de.target;
18734         if(!(ns instanceof Array)){
18735             ns = [ns];
18736         }
18737         var n;
18738         for(var i = 0, len = ns.length; i < len; i++){
18739             n = ns[i];
18740             if(p == "above"){
18741                 t.parentNode.insertBefore(n, t);
18742             }else if(p == "below"){
18743                 t.parentNode.insertBefore(n, t.nextSibling);
18744             }else{
18745                 t.appendChild(n);
18746             }
18747         }
18748         n.ui.focus();
18749         if(this.tree.hlDrop){
18750             n.ui.highlight();
18751         }
18752         t.ui.endDrop();
18753         this.tree.fireEvent("nodedrop", de);
18754     },
18755     
18756     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18757         if(this.tree.hlDrop){
18758             dropNode.ui.focus();
18759             dropNode.ui.highlight();
18760         }
18761         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18762     },
18763     
18764     getTree : function(){
18765         return this.tree;
18766     },
18767     
18768     removeDropIndicators : function(n){
18769         if(n && n.ddel){
18770             var el = n.ddel;
18771             Roo.fly(el).removeClass([
18772                     "x-tree-drag-insert-above",
18773                     "x-tree-drag-insert-below",
18774                     "x-tree-drag-append"]);
18775             this.lastInsertClass = "_noclass";
18776         }
18777     },
18778     
18779     beforeDragDrop : function(target, e, id){
18780         this.cancelExpand();
18781         return true;
18782     },
18783     
18784     afterRepair : function(data){
18785         if(data && Roo.enableFx){
18786             data.node.ui.highlight();
18787         }
18788         this.hideProxy();
18789     }    
18790 });
18791
18792 }
18793 /*
18794  * Based on:
18795  * Ext JS Library 1.1.1
18796  * Copyright(c) 2006-2007, Ext JS, LLC.
18797  *
18798  * Originally Released Under LGPL - original licence link has changed is not relivant.
18799  *
18800  * Fork - LGPL
18801  * <script type="text/javascript">
18802  */
18803  
18804
18805 if(Roo.dd.DragZone){
18806 Roo.tree.TreeDragZone = function(tree, config){
18807     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18808     this.tree = tree;
18809 };
18810
18811 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18812     ddGroup : "TreeDD",
18813     
18814     onBeforeDrag : function(data, e){
18815         var n = data.node;
18816         return n && n.draggable && !n.disabled;
18817     },
18818     
18819     onInitDrag : function(e){
18820         var data = this.dragData;
18821         this.tree.getSelectionModel().select(data.node);
18822         this.proxy.update("");
18823         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18824         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18825     },
18826     
18827     getRepairXY : function(e, data){
18828         return data.node.ui.getDDRepairXY();
18829     },
18830     
18831     onEndDrag : function(data, e){
18832         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18833     },
18834     
18835     onValidDrop : function(dd, e, id){
18836         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18837         this.hideProxy();
18838     },
18839     
18840     beforeInvalidDrop : function(e, id){
18841         // this scrolls the original position back into view
18842         var sm = this.tree.getSelectionModel();
18843         sm.clearSelections();
18844         sm.select(this.dragData.node);
18845     }
18846 });
18847 }/*
18848  * Based on:
18849  * Ext JS Library 1.1.1
18850  * Copyright(c) 2006-2007, Ext JS, LLC.
18851  *
18852  * Originally Released Under LGPL - original licence link has changed is not relivant.
18853  *
18854  * Fork - LGPL
18855  * <script type="text/javascript">
18856  */
18857 /**
18858  * @class Roo.tree.TreeEditor
18859  * @extends Roo.Editor
18860  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18861  * as the editor field.
18862  * @constructor
18863  * @param {TreePanel} tree
18864  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18865  */
18866 Roo.tree.TreeEditor = function(tree, config){
18867     config = config || {};
18868     var field = config.events ? config : new Roo.form.TextField(config);
18869     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18870
18871     this.tree = tree;
18872
18873     tree.on('beforeclick', this.beforeNodeClick, this);
18874     tree.getTreeEl().on('mousedown', this.hide, this);
18875     this.on('complete', this.updateNode, this);
18876     this.on('beforestartedit', this.fitToTree, this);
18877     this.on('startedit', this.bindScroll, this, {delay:10});
18878     this.on('specialkey', this.onSpecialKey, this);
18879 };
18880
18881 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18882     /**
18883      * @cfg {String} alignment
18884      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18885      */
18886     alignment: "l-l",
18887     // inherit
18888     autoSize: false,
18889     /**
18890      * @cfg {Boolean} hideEl
18891      * True to hide the bound element while the editor is displayed (defaults to false)
18892      */
18893     hideEl : false,
18894     /**
18895      * @cfg {String} cls
18896      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18897      */
18898     cls: "x-small-editor x-tree-editor",
18899     /**
18900      * @cfg {Boolean} shim
18901      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18902      */
18903     shim:false,
18904     // inherit
18905     shadow:"frame",
18906     /**
18907      * @cfg {Number} maxWidth
18908      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18909      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18910      * scroll and client offsets into account prior to each edit.
18911      */
18912     maxWidth: 250,
18913
18914     editDelay : 350,
18915
18916     // private
18917     fitToTree : function(ed, el){
18918         var td = this.tree.getTreeEl().dom, nd = el.dom;
18919         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18920             td.scrollLeft = nd.offsetLeft;
18921         }
18922         var w = Math.min(
18923                 this.maxWidth,
18924                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18925         this.setSize(w, '');
18926     },
18927
18928     // private
18929     triggerEdit : function(node){
18930         this.completeEdit();
18931         this.editNode = node;
18932         this.startEdit(node.ui.textNode, node.text);
18933     },
18934
18935     // private
18936     bindScroll : function(){
18937         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18938     },
18939
18940     // private
18941     beforeNodeClick : function(node, e){
18942         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18943         this.lastClick = new Date();
18944         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18945             e.stopEvent();
18946             this.triggerEdit(node);
18947             return false;
18948         }
18949     },
18950
18951     // private
18952     updateNode : function(ed, value){
18953         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18954         this.editNode.setText(value);
18955     },
18956
18957     // private
18958     onHide : function(){
18959         Roo.tree.TreeEditor.superclass.onHide.call(this);
18960         if(this.editNode){
18961             this.editNode.ui.focus();
18962         }
18963     },
18964
18965     // private
18966     onSpecialKey : function(field, e){
18967         var k = e.getKey();
18968         if(k == e.ESC){
18969             e.stopEvent();
18970             this.cancelEdit();
18971         }else if(k == e.ENTER && !e.hasModifier()){
18972             e.stopEvent();
18973             this.completeEdit();
18974         }
18975     }
18976 });//<Script type="text/javascript">
18977 /*
18978  * Based on:
18979  * Ext JS Library 1.1.1
18980  * Copyright(c) 2006-2007, Ext JS, LLC.
18981  *
18982  * Originally Released Under LGPL - original licence link has changed is not relivant.
18983  *
18984  * Fork - LGPL
18985  * <script type="text/javascript">
18986  */
18987  
18988 /**
18989  * Not documented??? - probably should be...
18990  */
18991
18992 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18993     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18994     
18995     renderElements : function(n, a, targetNode, bulkRender){
18996         //consel.log("renderElements?");
18997         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18998
18999         var t = n.getOwnerTree();
19000         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19001         
19002         var cols = t.columns;
19003         var bw = t.borderWidth;
19004         var c = cols[0];
19005         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19006          var cb = typeof a.checked == "boolean";
19007         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19008         var colcls = 'x-t-' + tid + '-c0';
19009         var buf = [
19010             '<li class="x-tree-node">',
19011             
19012                 
19013                 '<div class="x-tree-node-el ', a.cls,'">',
19014                     // extran...
19015                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19016                 
19017                 
19018                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19019                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19020                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19021                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19022                            (a.iconCls ? ' '+a.iconCls : ''),
19023                            '" unselectable="on" />',
19024                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19025                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19026                              
19027                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19028                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19029                             '<span unselectable="on" qtip="' + tx + '">',
19030                              tx,
19031                              '</span></a>' ,
19032                     '</div>',
19033                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19034                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19035                  ];
19036         for(var i = 1, len = cols.length; i < len; i++){
19037             c = cols[i];
19038             colcls = 'x-t-' + tid + '-c' +i;
19039             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19040             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19041                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19042                       "</div>");
19043          }
19044          
19045          buf.push(
19046             '</a>',
19047             '<div class="x-clear"></div></div>',
19048             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19049             "</li>");
19050         
19051         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19052             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19053                                 n.nextSibling.ui.getEl(), buf.join(""));
19054         }else{
19055             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19056         }
19057         var el = this.wrap.firstChild;
19058         this.elRow = el;
19059         this.elNode = el.firstChild;
19060         this.ranchor = el.childNodes[1];
19061         this.ctNode = this.wrap.childNodes[1];
19062         var cs = el.firstChild.childNodes;
19063         this.indentNode = cs[0];
19064         this.ecNode = cs[1];
19065         this.iconNode = cs[2];
19066         var index = 3;
19067         if(cb){
19068             this.checkbox = cs[3];
19069             index++;
19070         }
19071         this.anchor = cs[index];
19072         
19073         this.textNode = cs[index].firstChild;
19074         
19075         //el.on("click", this.onClick, this);
19076         //el.on("dblclick", this.onDblClick, this);
19077         
19078         
19079        // console.log(this);
19080     },
19081     initEvents : function(){
19082         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19083         
19084             
19085         var a = this.ranchor;
19086
19087         var el = Roo.get(a);
19088
19089         if(Roo.isOpera){ // opera render bug ignores the CSS
19090             el.setStyle("text-decoration", "none");
19091         }
19092
19093         el.on("click", this.onClick, this);
19094         el.on("dblclick", this.onDblClick, this);
19095         el.on("contextmenu", this.onContextMenu, this);
19096         
19097     },
19098     
19099     /*onSelectedChange : function(state){
19100         if(state){
19101             this.focus();
19102             this.addClass("x-tree-selected");
19103         }else{
19104             //this.blur();
19105             this.removeClass("x-tree-selected");
19106         }
19107     },*/
19108     addClass : function(cls){
19109         if(this.elRow){
19110             Roo.fly(this.elRow).addClass(cls);
19111         }
19112         
19113     },
19114     
19115     
19116     removeClass : function(cls){
19117         if(this.elRow){
19118             Roo.fly(this.elRow).removeClass(cls);
19119         }
19120     }
19121
19122     
19123     
19124 });//<Script type="text/javascript">
19125
19126 /*
19127  * Based on:
19128  * Ext JS Library 1.1.1
19129  * Copyright(c) 2006-2007, Ext JS, LLC.
19130  *
19131  * Originally Released Under LGPL - original licence link has changed is not relivant.
19132  *
19133  * Fork - LGPL
19134  * <script type="text/javascript">
19135  */
19136  
19137
19138 /**
19139  * @class Roo.tree.ColumnTree
19140  * @extends Roo.data.TreePanel
19141  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19142  * @cfg {int} borderWidth  compined right/left border allowance
19143  * @constructor
19144  * @param {String/HTMLElement/Element} el The container element
19145  * @param {Object} config
19146  */
19147 Roo.tree.ColumnTree =  function(el, config)
19148 {
19149    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19150    this.addEvents({
19151         /**
19152         * @event resize
19153         * Fire this event on a container when it resizes
19154         * @param {int} w Width
19155         * @param {int} h Height
19156         */
19157        "resize" : true
19158     });
19159     this.on('resize', this.onResize, this);
19160 };
19161
19162 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19163     //lines:false,
19164     
19165     
19166     borderWidth: Roo.isBorderBox ? 0 : 2, 
19167     headEls : false,
19168     
19169     render : function(){
19170         // add the header.....
19171        
19172         Roo.tree.ColumnTree.superclass.render.apply(this);
19173         
19174         this.el.addClass('x-column-tree');
19175         
19176         this.headers = this.el.createChild(
19177             {cls:'x-tree-headers'},this.innerCt.dom);
19178    
19179         var cols = this.columns, c;
19180         var totalWidth = 0;
19181         this.headEls = [];
19182         var  len = cols.length;
19183         for(var i = 0; i < len; i++){
19184              c = cols[i];
19185              totalWidth += c.width;
19186             this.headEls.push(this.headers.createChild({
19187                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19188                  cn: {
19189                      cls:'x-tree-hd-text',
19190                      html: c.header
19191                  },
19192                  style:'width:'+(c.width-this.borderWidth)+'px;'
19193              }));
19194         }
19195         this.headers.createChild({cls:'x-clear'});
19196         // prevent floats from wrapping when clipped
19197         this.headers.setWidth(totalWidth);
19198         //this.innerCt.setWidth(totalWidth);
19199         this.innerCt.setStyle({ overflow: 'auto' });
19200         this.onResize(this.width, this.height);
19201              
19202         
19203     },
19204     onResize : function(w,h)
19205     {
19206         this.height = h;
19207         this.width = w;
19208         // resize cols..
19209         this.innerCt.setWidth(this.width);
19210         this.innerCt.setHeight(this.height-20);
19211         
19212         // headers...
19213         var cols = this.columns, c;
19214         var totalWidth = 0;
19215         var expEl = false;
19216         var len = cols.length;
19217         for(var i = 0; i < len; i++){
19218             c = cols[i];
19219             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19220                 // it's the expander..
19221                 expEl  = this.headEls[i];
19222                 continue;
19223             }
19224             totalWidth += c.width;
19225             
19226         }
19227         if (expEl) {
19228             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19229         }
19230         this.headers.setWidth(w-20);
19231
19232         
19233         
19234         
19235     }
19236 });
19237 /*
19238  * Based on:
19239  * Ext JS Library 1.1.1
19240  * Copyright(c) 2006-2007, Ext JS, LLC.
19241  *
19242  * Originally Released Under LGPL - original licence link has changed is not relivant.
19243  *
19244  * Fork - LGPL
19245  * <script type="text/javascript">
19246  */
19247  
19248 /**
19249  * @class Roo.menu.Menu
19250  * @extends Roo.util.Observable
19251  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19252  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19253  * @constructor
19254  * Creates a new Menu
19255  * @param {Object} config Configuration options
19256  */
19257 Roo.menu.Menu = function(config){
19258     Roo.apply(this, config);
19259     this.id = this.id || Roo.id();
19260     this.addEvents({
19261         /**
19262          * @event beforeshow
19263          * Fires before this menu is displayed
19264          * @param {Roo.menu.Menu} this
19265          */
19266         beforeshow : true,
19267         /**
19268          * @event beforehide
19269          * Fires before this menu is hidden
19270          * @param {Roo.menu.Menu} this
19271          */
19272         beforehide : true,
19273         /**
19274          * @event show
19275          * Fires after this menu is displayed
19276          * @param {Roo.menu.Menu} this
19277          */
19278         show : true,
19279         /**
19280          * @event hide
19281          * Fires after this menu is hidden
19282          * @param {Roo.menu.Menu} this
19283          */
19284         hide : true,
19285         /**
19286          * @event click
19287          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19288          * @param {Roo.menu.Menu} this
19289          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19290          * @param {Roo.EventObject} e
19291          */
19292         click : true,
19293         /**
19294          * @event mouseover
19295          * Fires when the mouse is hovering over this menu
19296          * @param {Roo.menu.Menu} this
19297          * @param {Roo.EventObject} e
19298          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19299          */
19300         mouseover : true,
19301         /**
19302          * @event mouseout
19303          * Fires when the mouse exits this menu
19304          * @param {Roo.menu.Menu} this
19305          * @param {Roo.EventObject} e
19306          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19307          */
19308         mouseout : true,
19309         /**
19310          * @event itemclick
19311          * Fires when a menu item contained in this menu is clicked
19312          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19313          * @param {Roo.EventObject} e
19314          */
19315         itemclick: true
19316     });
19317     if (this.registerMenu) {
19318         Roo.menu.MenuMgr.register(this);
19319     }
19320     
19321     var mis = this.items;
19322     this.items = new Roo.util.MixedCollection();
19323     if(mis){
19324         this.add.apply(this, mis);
19325     }
19326 };
19327
19328 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19329     /**
19330      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19331      */
19332     minWidth : 120,
19333     /**
19334      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19335      * for bottom-right shadow (defaults to "sides")
19336      */
19337     shadow : "sides",
19338     /**
19339      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19340      * this menu (defaults to "tl-tr?")
19341      */
19342     subMenuAlign : "tl-tr?",
19343     /**
19344      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19345      * relative to its element of origin (defaults to "tl-bl?")
19346      */
19347     defaultAlign : "tl-bl?",
19348     /**
19349      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19350      */
19351     allowOtherMenus : false,
19352     /**
19353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19354      */
19355     registerMenu : true,
19356
19357     hidden:true,
19358
19359     // private
19360     render : function(){
19361         if(this.el){
19362             return;
19363         }
19364         var el = this.el = new Roo.Layer({
19365             cls: "x-menu",
19366             shadow:this.shadow,
19367             constrain: false,
19368             parentEl: this.parentEl || document.body,
19369             zindex:15000
19370         });
19371
19372         this.keyNav = new Roo.menu.MenuNav(this);
19373
19374         if(this.plain){
19375             el.addClass("x-menu-plain");
19376         }
19377         if(this.cls){
19378             el.addClass(this.cls);
19379         }
19380         // generic focus element
19381         this.focusEl = el.createChild({
19382             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19383         });
19384         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19385         ul.on("click", this.onClick, this);
19386         ul.on("mouseover", this.onMouseOver, this);
19387         ul.on("mouseout", this.onMouseOut, this);
19388         this.items.each(function(item){
19389             var li = document.createElement("li");
19390             li.className = "x-menu-list-item";
19391             ul.dom.appendChild(li);
19392             item.render(li, this);
19393         }, this);
19394         this.ul = ul;
19395         this.autoWidth();
19396     },
19397
19398     // private
19399     autoWidth : function(){
19400         var el = this.el, ul = this.ul;
19401         if(!el){
19402             return;
19403         }
19404         var w = this.width;
19405         if(w){
19406             el.setWidth(w);
19407         }else if(Roo.isIE){
19408             el.setWidth(this.minWidth);
19409             var t = el.dom.offsetWidth; // force recalc
19410             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19411         }
19412     },
19413
19414     // private
19415     delayAutoWidth : function(){
19416         if(this.rendered){
19417             if(!this.awTask){
19418                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19419             }
19420             this.awTask.delay(20);
19421         }
19422     },
19423
19424     // private
19425     findTargetItem : function(e){
19426         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19427         if(t && t.menuItemId){
19428             return this.items.get(t.menuItemId);
19429         }
19430     },
19431
19432     // private
19433     onClick : function(e){
19434         var t;
19435         if(t = this.findTargetItem(e)){
19436             t.onClick(e);
19437             this.fireEvent("click", this, t, e);
19438         }
19439     },
19440
19441     // private
19442     setActiveItem : function(item, autoExpand){
19443         if(item != this.activeItem){
19444             if(this.activeItem){
19445                 this.activeItem.deactivate();
19446             }
19447             this.activeItem = item;
19448             item.activate(autoExpand);
19449         }else if(autoExpand){
19450             item.expandMenu();
19451         }
19452     },
19453
19454     // private
19455     tryActivate : function(start, step){
19456         var items = this.items;
19457         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19458             var item = items.get(i);
19459             if(!item.disabled && item.canActivate){
19460                 this.setActiveItem(item, false);
19461                 return item;
19462             }
19463         }
19464         return false;
19465     },
19466
19467     // private
19468     onMouseOver : function(e){
19469         var t;
19470         if(t = this.findTargetItem(e)){
19471             if(t.canActivate && !t.disabled){
19472                 this.setActiveItem(t, true);
19473             }
19474         }
19475         this.fireEvent("mouseover", this, e, t);
19476     },
19477
19478     // private
19479     onMouseOut : function(e){
19480         var t;
19481         if(t = this.findTargetItem(e)){
19482             if(t == this.activeItem && t.shouldDeactivate(e)){
19483                 this.activeItem.deactivate();
19484                 delete this.activeItem;
19485             }
19486         }
19487         this.fireEvent("mouseout", this, e, t);
19488     },
19489
19490     /**
19491      * Read-only.  Returns true if the menu is currently displayed, else false.
19492      * @type Boolean
19493      */
19494     isVisible : function(){
19495         return this.el && !this.hidden;
19496     },
19497
19498     /**
19499      * Displays this menu relative to another element
19500      * @param {String/HTMLElement/Roo.Element} element The element to align to
19501      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19502      * the element (defaults to this.defaultAlign)
19503      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19504      */
19505     show : function(el, pos, parentMenu){
19506         this.parentMenu = parentMenu;
19507         if(!this.el){
19508             this.render();
19509         }
19510         this.fireEvent("beforeshow", this);
19511         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19512     },
19513
19514     /**
19515      * Displays this menu at a specific xy position
19516      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19517      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19518      */
19519     showAt : function(xy, parentMenu, /* private: */_e){
19520         this.parentMenu = parentMenu;
19521         if(!this.el){
19522             this.render();
19523         }
19524         if(_e !== false){
19525             this.fireEvent("beforeshow", this);
19526             xy = this.el.adjustForConstraints(xy);
19527         }
19528         this.el.setXY(xy);
19529         this.el.show();
19530         this.hidden = false;
19531         this.focus();
19532         this.fireEvent("show", this);
19533     },
19534
19535     focus : function(){
19536         if(!this.hidden){
19537             this.doFocus.defer(50, this);
19538         }
19539     },
19540
19541     doFocus : function(){
19542         if(!this.hidden){
19543             this.focusEl.focus();
19544         }
19545     },
19546
19547     /**
19548      * Hides this menu and optionally all parent menus
19549      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19550      */
19551     hide : function(deep){
19552         if(this.el && this.isVisible()){
19553             this.fireEvent("beforehide", this);
19554             if(this.activeItem){
19555                 this.activeItem.deactivate();
19556                 this.activeItem = null;
19557             }
19558             this.el.hide();
19559             this.hidden = true;
19560             this.fireEvent("hide", this);
19561         }
19562         if(deep === true && this.parentMenu){
19563             this.parentMenu.hide(true);
19564         }
19565     },
19566
19567     /**
19568      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19569      * Any of the following are valid:
19570      * <ul>
19571      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19572      * <li>An HTMLElement object which will be converted to a menu item</li>
19573      * <li>A menu item config object that will be created as a new menu item</li>
19574      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19575      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19576      * </ul>
19577      * Usage:
19578      * <pre><code>
19579 // Create the menu
19580 var menu = new Roo.menu.Menu();
19581
19582 // Create a menu item to add by reference
19583 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19584
19585 // Add a bunch of items at once using different methods.
19586 // Only the last item added will be returned.
19587 var item = menu.add(
19588     menuItem,                // add existing item by ref
19589     'Dynamic Item',          // new TextItem
19590     '-',                     // new separator
19591     { text: 'Config Item' }  // new item by config
19592 );
19593 </code></pre>
19594      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19595      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19596      */
19597     add : function(){
19598         var a = arguments, l = a.length, item;
19599         for(var i = 0; i < l; i++){
19600             var el = a[i];
19601             if ((typeof(el) == "object") && el.xtype && el.xns) {
19602                 el = Roo.factory(el, Roo.menu);
19603             }
19604             
19605             if(el.render){ // some kind of Item
19606                 item = this.addItem(el);
19607             }else if(typeof el == "string"){ // string
19608                 if(el == "separator" || el == "-"){
19609                     item = this.addSeparator();
19610                 }else{
19611                     item = this.addText(el);
19612                 }
19613             }else if(el.tagName || el.el){ // element
19614                 item = this.addElement(el);
19615             }else if(typeof el == "object"){ // must be menu item config?
19616                 item = this.addMenuItem(el);
19617             }
19618         }
19619         return item;
19620     },
19621
19622     /**
19623      * Returns this menu's underlying {@link Roo.Element} object
19624      * @return {Roo.Element} The element
19625      */
19626     getEl : function(){
19627         if(!this.el){
19628             this.render();
19629         }
19630         return this.el;
19631     },
19632
19633     /**
19634      * Adds a separator bar to the menu
19635      * @return {Roo.menu.Item} The menu item that was added
19636      */
19637     addSeparator : function(){
19638         return this.addItem(new Roo.menu.Separator());
19639     },
19640
19641     /**
19642      * Adds an {@link Roo.Element} object to the menu
19643      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19644      * @return {Roo.menu.Item} The menu item that was added
19645      */
19646     addElement : function(el){
19647         return this.addItem(new Roo.menu.BaseItem(el));
19648     },
19649
19650     /**
19651      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19652      * @param {Roo.menu.Item} item The menu item to add
19653      * @return {Roo.menu.Item} The menu item that was added
19654      */
19655     addItem : function(item){
19656         this.items.add(item);
19657         if(this.ul){
19658             var li = document.createElement("li");
19659             li.className = "x-menu-list-item";
19660             this.ul.dom.appendChild(li);
19661             item.render(li, this);
19662             this.delayAutoWidth();
19663         }
19664         return item;
19665     },
19666
19667     /**
19668      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19669      * @param {Object} config A MenuItem config object
19670      * @return {Roo.menu.Item} The menu item that was added
19671      */
19672     addMenuItem : function(config){
19673         if(!(config instanceof Roo.menu.Item)){
19674             if(typeof config.checked == "boolean"){ // must be check menu item config?
19675                 config = new Roo.menu.CheckItem(config);
19676             }else{
19677                 config = new Roo.menu.Item(config);
19678             }
19679         }
19680         return this.addItem(config);
19681     },
19682
19683     /**
19684      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19685      * @param {String} text The text to display in the menu item
19686      * @return {Roo.menu.Item} The menu item that was added
19687      */
19688     addText : function(text){
19689         return this.addItem(new Roo.menu.TextItem({ text : text }));
19690     },
19691
19692     /**
19693      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19694      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19695      * @param {Roo.menu.Item} item The menu item to add
19696      * @return {Roo.menu.Item} The menu item that was added
19697      */
19698     insert : function(index, item){
19699         this.items.insert(index, item);
19700         if(this.ul){
19701             var li = document.createElement("li");
19702             li.className = "x-menu-list-item";
19703             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19704             item.render(li, this);
19705             this.delayAutoWidth();
19706         }
19707         return item;
19708     },
19709
19710     /**
19711      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19712      * @param {Roo.menu.Item} item The menu item to remove
19713      */
19714     remove : function(item){
19715         this.items.removeKey(item.id);
19716         item.destroy();
19717     },
19718
19719     /**
19720      * Removes and destroys all items in the menu
19721      */
19722     removeAll : function(){
19723         var f;
19724         while(f = this.items.first()){
19725             this.remove(f);
19726         }
19727     }
19728 });
19729
19730 // MenuNav is a private utility class used internally by the Menu
19731 Roo.menu.MenuNav = function(menu){
19732     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19733     this.scope = this.menu = menu;
19734 };
19735
19736 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19737     doRelay : function(e, h){
19738         var k = e.getKey();
19739         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19740             this.menu.tryActivate(0, 1);
19741             return false;
19742         }
19743         return h.call(this.scope || this, e, this.menu);
19744     },
19745
19746     up : function(e, m){
19747         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19748             m.tryActivate(m.items.length-1, -1);
19749         }
19750     },
19751
19752     down : function(e, m){
19753         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19754             m.tryActivate(0, 1);
19755         }
19756     },
19757
19758     right : function(e, m){
19759         if(m.activeItem){
19760             m.activeItem.expandMenu(true);
19761         }
19762     },
19763
19764     left : function(e, m){
19765         m.hide();
19766         if(m.parentMenu && m.parentMenu.activeItem){
19767             m.parentMenu.activeItem.activate();
19768         }
19769     },
19770
19771     enter : function(e, m){
19772         if(m.activeItem){
19773             e.stopPropagation();
19774             m.activeItem.onClick(e);
19775             m.fireEvent("click", this, m.activeItem);
19776             return true;
19777         }
19778     }
19779 });/*
19780  * Based on:
19781  * Ext JS Library 1.1.1
19782  * Copyright(c) 2006-2007, Ext JS, LLC.
19783  *
19784  * Originally Released Under LGPL - original licence link has changed is not relivant.
19785  *
19786  * Fork - LGPL
19787  * <script type="text/javascript">
19788  */
19789  
19790 /**
19791  * @class Roo.menu.MenuMgr
19792  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19793  * @singleton
19794  */
19795 Roo.menu.MenuMgr = function(){
19796    var menus, active, groups = {}, attached = false, lastShow = new Date();
19797
19798    // private - called when first menu is created
19799    function init(){
19800        menus = {};
19801        active = new Roo.util.MixedCollection();
19802        Roo.get(document).addKeyListener(27, function(){
19803            if(active.length > 0){
19804                hideAll();
19805            }
19806        });
19807    }
19808
19809    // private
19810    function hideAll(){
19811        if(active && active.length > 0){
19812            var c = active.clone();
19813            c.each(function(m){
19814                m.hide();
19815            });
19816        }
19817    }
19818
19819    // private
19820    function onHide(m){
19821        active.remove(m);
19822        if(active.length < 1){
19823            Roo.get(document).un("mousedown", onMouseDown);
19824            attached = false;
19825        }
19826    }
19827
19828    // private
19829    function onShow(m){
19830        var last = active.last();
19831        lastShow = new Date();
19832        active.add(m);
19833        if(!attached){
19834            Roo.get(document).on("mousedown", onMouseDown);
19835            attached = true;
19836        }
19837        if(m.parentMenu){
19838           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19839           m.parentMenu.activeChild = m;
19840        }else if(last && last.isVisible()){
19841           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19842        }
19843    }
19844
19845    // private
19846    function onBeforeHide(m){
19847        if(m.activeChild){
19848            m.activeChild.hide();
19849        }
19850        if(m.autoHideTimer){
19851            clearTimeout(m.autoHideTimer);
19852            delete m.autoHideTimer;
19853        }
19854    }
19855
19856    // private
19857    function onBeforeShow(m){
19858        var pm = m.parentMenu;
19859        if(!pm && !m.allowOtherMenus){
19860            hideAll();
19861        }else if(pm && pm.activeChild && active != m){
19862            pm.activeChild.hide();
19863        }
19864    }
19865
19866    // private
19867    function onMouseDown(e){
19868        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19869            hideAll();
19870        }
19871    }
19872
19873    // private
19874    function onBeforeCheck(mi, state){
19875        if(state){
19876            var g = groups[mi.group];
19877            for(var i = 0, l = g.length; i < l; i++){
19878                if(g[i] != mi){
19879                    g[i].setChecked(false);
19880                }
19881            }
19882        }
19883    }
19884
19885    return {
19886
19887        /**
19888         * Hides all menus that are currently visible
19889         */
19890        hideAll : function(){
19891             hideAll();  
19892        },
19893
19894        // private
19895        register : function(menu){
19896            if(!menus){
19897                init();
19898            }
19899            menus[menu.id] = menu;
19900            menu.on("beforehide", onBeforeHide);
19901            menu.on("hide", onHide);
19902            menu.on("beforeshow", onBeforeShow);
19903            menu.on("show", onShow);
19904            var g = menu.group;
19905            if(g && menu.events["checkchange"]){
19906                if(!groups[g]){
19907                    groups[g] = [];
19908                }
19909                groups[g].push(menu);
19910                menu.on("checkchange", onCheck);
19911            }
19912        },
19913
19914         /**
19915          * Returns a {@link Roo.menu.Menu} object
19916          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19917          * be used to generate and return a new Menu instance.
19918          */
19919        get : function(menu){
19920            if(typeof menu == "string"){ // menu id
19921                return menus[menu];
19922            }else if(menu.events){  // menu instance
19923                return menu;
19924            }else if(typeof menu.length == 'number'){ // array of menu items?
19925                return new Roo.menu.Menu({items:menu});
19926            }else{ // otherwise, must be a config
19927                return new Roo.menu.Menu(menu);
19928            }
19929        },
19930
19931        // private
19932        unregister : function(menu){
19933            delete menus[menu.id];
19934            menu.un("beforehide", onBeforeHide);
19935            menu.un("hide", onHide);
19936            menu.un("beforeshow", onBeforeShow);
19937            menu.un("show", onShow);
19938            var g = menu.group;
19939            if(g && menu.events["checkchange"]){
19940                groups[g].remove(menu);
19941                menu.un("checkchange", onCheck);
19942            }
19943        },
19944
19945        // private
19946        registerCheckable : function(menuItem){
19947            var g = menuItem.group;
19948            if(g){
19949                if(!groups[g]){
19950                    groups[g] = [];
19951                }
19952                groups[g].push(menuItem);
19953                menuItem.on("beforecheckchange", onBeforeCheck);
19954            }
19955        },
19956
19957        // private
19958        unregisterCheckable : function(menuItem){
19959            var g = menuItem.group;
19960            if(g){
19961                groups[g].remove(menuItem);
19962                menuItem.un("beforecheckchange", onBeforeCheck);
19963            }
19964        }
19965    };
19966 }();/*
19967  * Based on:
19968  * Ext JS Library 1.1.1
19969  * Copyright(c) 2006-2007, Ext JS, LLC.
19970  *
19971  * Originally Released Under LGPL - original licence link has changed is not relivant.
19972  *
19973  * Fork - LGPL
19974  * <script type="text/javascript">
19975  */
19976  
19977
19978 /**
19979  * @class Roo.menu.BaseItem
19980  * @extends Roo.Component
19981  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19982  * management and base configuration options shared by all menu components.
19983  * @constructor
19984  * Creates a new BaseItem
19985  * @param {Object} config Configuration options
19986  */
19987 Roo.menu.BaseItem = function(config){
19988     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19989
19990     this.addEvents({
19991         /**
19992          * @event click
19993          * Fires when this item is clicked
19994          * @param {Roo.menu.BaseItem} this
19995          * @param {Roo.EventObject} e
19996          */
19997         click: true,
19998         /**
19999          * @event activate
20000          * Fires when this item is activated
20001          * @param {Roo.menu.BaseItem} this
20002          */
20003         activate : true,
20004         /**
20005          * @event deactivate
20006          * Fires when this item is deactivated
20007          * @param {Roo.menu.BaseItem} this
20008          */
20009         deactivate : true
20010     });
20011
20012     if(this.handler){
20013         this.on("click", this.handler, this.scope, true);
20014     }
20015 };
20016
20017 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20018     /**
20019      * @cfg {Function} handler
20020      * A function that will handle the click event of this menu item (defaults to undefined)
20021      */
20022     /**
20023      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20024      */
20025     canActivate : false,
20026     /**
20027      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20028      */
20029     activeClass : "x-menu-item-active",
20030     /**
20031      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20032      */
20033     hideOnClick : true,
20034     /**
20035      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20036      */
20037     hideDelay : 100,
20038
20039     // private
20040     ctype: "Roo.menu.BaseItem",
20041
20042     // private
20043     actionMode : "container",
20044
20045     // private
20046     render : function(container, parentMenu){
20047         this.parentMenu = parentMenu;
20048         Roo.menu.BaseItem.superclass.render.call(this, container);
20049         this.container.menuItemId = this.id;
20050     },
20051
20052     // private
20053     onRender : function(container, position){
20054         this.el = Roo.get(this.el);
20055         container.dom.appendChild(this.el.dom);
20056     },
20057
20058     // private
20059     onClick : function(e){
20060         if(!this.disabled && this.fireEvent("click", this, e) !== false
20061                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20062             this.handleClick(e);
20063         }else{
20064             e.stopEvent();
20065         }
20066     },
20067
20068     // private
20069     activate : function(){
20070         if(this.disabled){
20071             return false;
20072         }
20073         var li = this.container;
20074         li.addClass(this.activeClass);
20075         this.region = li.getRegion().adjust(2, 2, -2, -2);
20076         this.fireEvent("activate", this);
20077         return true;
20078     },
20079
20080     // private
20081     deactivate : function(){
20082         this.container.removeClass(this.activeClass);
20083         this.fireEvent("deactivate", this);
20084     },
20085
20086     // private
20087     shouldDeactivate : function(e){
20088         return !this.region || !this.region.contains(e.getPoint());
20089     },
20090
20091     // private
20092     handleClick : function(e){
20093         if(this.hideOnClick){
20094             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20095         }
20096     },
20097
20098     // private
20099     expandMenu : function(autoActivate){
20100         // do nothing
20101     },
20102
20103     // private
20104     hideMenu : function(){
20105         // do nothing
20106     }
20107 });/*
20108  * Based on:
20109  * Ext JS Library 1.1.1
20110  * Copyright(c) 2006-2007, Ext JS, LLC.
20111  *
20112  * Originally Released Under LGPL - original licence link has changed is not relivant.
20113  *
20114  * Fork - LGPL
20115  * <script type="text/javascript">
20116  */
20117  
20118 /**
20119  * @class Roo.menu.Adapter
20120  * @extends Roo.menu.BaseItem
20121  * 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.
20122  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20123  * @constructor
20124  * Creates a new Adapter
20125  * @param {Object} config Configuration options
20126  */
20127 Roo.menu.Adapter = function(component, config){
20128     Roo.menu.Adapter.superclass.constructor.call(this, config);
20129     this.component = component;
20130 };
20131 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20132     // private
20133     canActivate : true,
20134
20135     // private
20136     onRender : function(container, position){
20137         this.component.render(container);
20138         this.el = this.component.getEl();
20139     },
20140
20141     // private
20142     activate : function(){
20143         if(this.disabled){
20144             return false;
20145         }
20146         this.component.focus();
20147         this.fireEvent("activate", this);
20148         return true;
20149     },
20150
20151     // private
20152     deactivate : function(){
20153         this.fireEvent("deactivate", this);
20154     },
20155
20156     // private
20157     disable : function(){
20158         this.component.disable();
20159         Roo.menu.Adapter.superclass.disable.call(this);
20160     },
20161
20162     // private
20163     enable : function(){
20164         this.component.enable();
20165         Roo.menu.Adapter.superclass.enable.call(this);
20166     }
20167 });/*
20168  * Based on:
20169  * Ext JS Library 1.1.1
20170  * Copyright(c) 2006-2007, Ext JS, LLC.
20171  *
20172  * Originally Released Under LGPL - original licence link has changed is not relivant.
20173  *
20174  * Fork - LGPL
20175  * <script type="text/javascript">
20176  */
20177
20178 /**
20179  * @class Roo.menu.TextItem
20180  * @extends Roo.menu.BaseItem
20181  * Adds a static text string to a menu, usually used as either a heading or group separator.
20182  * Note: old style constructor with text is still supported.
20183  * 
20184  * @constructor
20185  * Creates a new TextItem
20186  * @param {Object} cfg Configuration
20187  */
20188 Roo.menu.TextItem = function(cfg){
20189     if (typeof(cfg) == 'string') {
20190         this.text = cfg;
20191     } else {
20192         Roo.apply(this,cfg);
20193     }
20194     
20195     Roo.menu.TextItem.superclass.constructor.call(this);
20196 };
20197
20198 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20199     /**
20200      * @cfg {Boolean} text Text to show on item.
20201      */
20202     text : '',
20203     
20204     /**
20205      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20206      */
20207     hideOnClick : false,
20208     /**
20209      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20210      */
20211     itemCls : "x-menu-text",
20212
20213     // private
20214     onRender : function(){
20215         var s = document.createElement("span");
20216         s.className = this.itemCls;
20217         s.innerHTML = this.text;
20218         this.el = s;
20219         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20220     }
20221 });/*
20222  * Based on:
20223  * Ext JS Library 1.1.1
20224  * Copyright(c) 2006-2007, Ext JS, LLC.
20225  *
20226  * Originally Released Under LGPL - original licence link has changed is not relivant.
20227  *
20228  * Fork - LGPL
20229  * <script type="text/javascript">
20230  */
20231
20232 /**
20233  * @class Roo.menu.Separator
20234  * @extends Roo.menu.BaseItem
20235  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20236  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20237  * @constructor
20238  * @param {Object} config Configuration options
20239  */
20240 Roo.menu.Separator = function(config){
20241     Roo.menu.Separator.superclass.constructor.call(this, config);
20242 };
20243
20244 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20245     /**
20246      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20247      */
20248     itemCls : "x-menu-sep",
20249     /**
20250      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20251      */
20252     hideOnClick : false,
20253
20254     // private
20255     onRender : function(li){
20256         var s = document.createElement("span");
20257         s.className = this.itemCls;
20258         s.innerHTML = "&#160;";
20259         this.el = s;
20260         li.addClass("x-menu-sep-li");
20261         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20262     }
20263 });/*
20264  * Based on:
20265  * Ext JS Library 1.1.1
20266  * Copyright(c) 2006-2007, Ext JS, LLC.
20267  *
20268  * Originally Released Under LGPL - original licence link has changed is not relivant.
20269  *
20270  * Fork - LGPL
20271  * <script type="text/javascript">
20272  */
20273 /**
20274  * @class Roo.menu.Item
20275  * @extends Roo.menu.BaseItem
20276  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20277  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20278  * activation and click handling.
20279  * @constructor
20280  * Creates a new Item
20281  * @param {Object} config Configuration options
20282  */
20283 Roo.menu.Item = function(config){
20284     Roo.menu.Item.superclass.constructor.call(this, config);
20285     if(this.menu){
20286         this.menu = Roo.menu.MenuMgr.get(this.menu);
20287     }
20288 };
20289 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20290     
20291     /**
20292      * @cfg {String} text
20293      * The text to show on the menu item.
20294      */
20295     text: '',
20296      /**
20297      * @cfg {String} HTML to render in menu
20298      * The text to show on the menu item (HTML version).
20299      */
20300     html: '',
20301     /**
20302      * @cfg {String} icon
20303      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20304      */
20305     icon: undefined,
20306     /**
20307      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20308      */
20309     itemCls : "x-menu-item",
20310     /**
20311      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20312      */
20313     canActivate : true,
20314     /**
20315      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20316      */
20317     showDelay: 200,
20318     // doc'd in BaseItem
20319     hideDelay: 200,
20320
20321     // private
20322     ctype: "Roo.menu.Item",
20323     
20324     // private
20325     onRender : function(container, position){
20326         var el = document.createElement("a");
20327         el.hideFocus = true;
20328         el.unselectable = "on";
20329         el.href = this.href || "#";
20330         if(this.hrefTarget){
20331             el.target = this.hrefTarget;
20332         }
20333         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20334         
20335         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20336         
20337         el.innerHTML = String.format(
20338                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20339                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20340         this.el = el;
20341         Roo.menu.Item.superclass.onRender.call(this, container, position);
20342     },
20343
20344     /**
20345      * Sets the text to display in this menu item
20346      * @param {String} text The text to display
20347      * @param {Boolean} isHTML true to indicate text is pure html.
20348      */
20349     setText : function(text, isHTML){
20350         if (isHTML) {
20351             this.html = text;
20352         } else {
20353             this.text = text;
20354             this.html = '';
20355         }
20356         if(this.rendered){
20357             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20358      
20359             this.el.update(String.format(
20360                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20361                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20362             this.parentMenu.autoWidth();
20363         }
20364     },
20365
20366     // private
20367     handleClick : function(e){
20368         if(!this.href){ // if no link defined, stop the event automatically
20369             e.stopEvent();
20370         }
20371         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20372     },
20373
20374     // private
20375     activate : function(autoExpand){
20376         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20377             this.focus();
20378             if(autoExpand){
20379                 this.expandMenu();
20380             }
20381         }
20382         return true;
20383     },
20384
20385     // private
20386     shouldDeactivate : function(e){
20387         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20388             if(this.menu && this.menu.isVisible()){
20389                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20390             }
20391             return true;
20392         }
20393         return false;
20394     },
20395
20396     // private
20397     deactivate : function(){
20398         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20399         this.hideMenu();
20400     },
20401
20402     // private
20403     expandMenu : function(autoActivate){
20404         if(!this.disabled && this.menu){
20405             clearTimeout(this.hideTimer);
20406             delete this.hideTimer;
20407             if(!this.menu.isVisible() && !this.showTimer){
20408                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20409             }else if (this.menu.isVisible() && autoActivate){
20410                 this.menu.tryActivate(0, 1);
20411             }
20412         }
20413     },
20414
20415     // private
20416     deferExpand : function(autoActivate){
20417         delete this.showTimer;
20418         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20419         if(autoActivate){
20420             this.menu.tryActivate(0, 1);
20421         }
20422     },
20423
20424     // private
20425     hideMenu : function(){
20426         clearTimeout(this.showTimer);
20427         delete this.showTimer;
20428         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20429             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20430         }
20431     },
20432
20433     // private
20434     deferHide : function(){
20435         delete this.hideTimer;
20436         this.menu.hide();
20437     }
20438 });/*
20439  * Based on:
20440  * Ext JS Library 1.1.1
20441  * Copyright(c) 2006-2007, Ext JS, LLC.
20442  *
20443  * Originally Released Under LGPL - original licence link has changed is not relivant.
20444  *
20445  * Fork - LGPL
20446  * <script type="text/javascript">
20447  */
20448  
20449 /**
20450  * @class Roo.menu.CheckItem
20451  * @extends Roo.menu.Item
20452  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20453  * @constructor
20454  * Creates a new CheckItem
20455  * @param {Object} config Configuration options
20456  */
20457 Roo.menu.CheckItem = function(config){
20458     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20459     this.addEvents({
20460         /**
20461          * @event beforecheckchange
20462          * Fires before the checked value is set, providing an opportunity to cancel if needed
20463          * @param {Roo.menu.CheckItem} this
20464          * @param {Boolean} checked The new checked value that will be set
20465          */
20466         "beforecheckchange" : true,
20467         /**
20468          * @event checkchange
20469          * Fires after the checked value has been set
20470          * @param {Roo.menu.CheckItem} this
20471          * @param {Boolean} checked The checked value that was set
20472          */
20473         "checkchange" : true
20474     });
20475     if(this.checkHandler){
20476         this.on('checkchange', this.checkHandler, this.scope);
20477     }
20478 };
20479 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20480     /**
20481      * @cfg {String} group
20482      * All check items with the same group name will automatically be grouped into a single-select
20483      * radio button group (defaults to '')
20484      */
20485     /**
20486      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20487      */
20488     itemCls : "x-menu-item x-menu-check-item",
20489     /**
20490      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20491      */
20492     groupClass : "x-menu-group-item",
20493
20494     /**
20495      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20496      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20497      * initialized with checked = true will be rendered as checked.
20498      */
20499     checked: false,
20500
20501     // private
20502     ctype: "Roo.menu.CheckItem",
20503
20504     // private
20505     onRender : function(c){
20506         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20507         if(this.group){
20508             this.el.addClass(this.groupClass);
20509         }
20510         Roo.menu.MenuMgr.registerCheckable(this);
20511         if(this.checked){
20512             this.checked = false;
20513             this.setChecked(true, true);
20514         }
20515     },
20516
20517     // private
20518     destroy : function(){
20519         if(this.rendered){
20520             Roo.menu.MenuMgr.unregisterCheckable(this);
20521         }
20522         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20523     },
20524
20525     /**
20526      * Set the checked state of this item
20527      * @param {Boolean} checked The new checked value
20528      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20529      */
20530     setChecked : function(state, suppressEvent){
20531         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20532             if(this.container){
20533                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20534             }
20535             this.checked = state;
20536             if(suppressEvent !== true){
20537                 this.fireEvent("checkchange", this, state);
20538             }
20539         }
20540     },
20541
20542     // private
20543     handleClick : function(e){
20544        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20545            this.setChecked(!this.checked);
20546        }
20547        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20548     }
20549 });/*
20550  * Based on:
20551  * Ext JS Library 1.1.1
20552  * Copyright(c) 2006-2007, Ext JS, LLC.
20553  *
20554  * Originally Released Under LGPL - original licence link has changed is not relivant.
20555  *
20556  * Fork - LGPL
20557  * <script type="text/javascript">
20558  */
20559  
20560 /**
20561  * @class Roo.menu.DateItem
20562  * @extends Roo.menu.Adapter
20563  * A menu item that wraps the {@link Roo.DatPicker} component.
20564  * @constructor
20565  * Creates a new DateItem
20566  * @param {Object} config Configuration options
20567  */
20568 Roo.menu.DateItem = function(config){
20569     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20570     /** The Roo.DatePicker object @type Roo.DatePicker */
20571     this.picker = this.component;
20572     this.addEvents({select: true});
20573     
20574     this.picker.on("render", function(picker){
20575         picker.getEl().swallowEvent("click");
20576         picker.container.addClass("x-menu-date-item");
20577     });
20578
20579     this.picker.on("select", this.onSelect, this);
20580 };
20581
20582 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20583     // private
20584     onSelect : function(picker, date){
20585         this.fireEvent("select", this, date, picker);
20586         Roo.menu.DateItem.superclass.handleClick.call(this);
20587     }
20588 });/*
20589  * Based on:
20590  * Ext JS Library 1.1.1
20591  * Copyright(c) 2006-2007, Ext JS, LLC.
20592  *
20593  * Originally Released Under LGPL - original licence link has changed is not relivant.
20594  *
20595  * Fork - LGPL
20596  * <script type="text/javascript">
20597  */
20598  
20599 /**
20600  * @class Roo.menu.ColorItem
20601  * @extends Roo.menu.Adapter
20602  * A menu item that wraps the {@link Roo.ColorPalette} component.
20603  * @constructor
20604  * Creates a new ColorItem
20605  * @param {Object} config Configuration options
20606  */
20607 Roo.menu.ColorItem = function(config){
20608     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20609     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20610     this.palette = this.component;
20611     this.relayEvents(this.palette, ["select"]);
20612     if(this.selectHandler){
20613         this.on('select', this.selectHandler, this.scope);
20614     }
20615 };
20616 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20617  * Based on:
20618  * Ext JS Library 1.1.1
20619  * Copyright(c) 2006-2007, Ext JS, LLC.
20620  *
20621  * Originally Released Under LGPL - original licence link has changed is not relivant.
20622  *
20623  * Fork - LGPL
20624  * <script type="text/javascript">
20625  */
20626  
20627
20628 /**
20629  * @class Roo.menu.DateMenu
20630  * @extends Roo.menu.Menu
20631  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20632  * @constructor
20633  * Creates a new DateMenu
20634  * @param {Object} config Configuration options
20635  */
20636 Roo.menu.DateMenu = function(config){
20637     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20638     this.plain = true;
20639     var di = new Roo.menu.DateItem(config);
20640     this.add(di);
20641     /**
20642      * The {@link Roo.DatePicker} instance for this DateMenu
20643      * @type DatePicker
20644      */
20645     this.picker = di.picker;
20646     /**
20647      * @event select
20648      * @param {DatePicker} picker
20649      * @param {Date} date
20650      */
20651     this.relayEvents(di, ["select"]);
20652
20653     this.on('beforeshow', function(){
20654         if(this.picker){
20655             this.picker.hideMonthPicker(true);
20656         }
20657     }, this);
20658 };
20659 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20660     cls:'x-date-menu'
20661 });/*
20662  * Based on:
20663  * Ext JS Library 1.1.1
20664  * Copyright(c) 2006-2007, Ext JS, LLC.
20665  *
20666  * Originally Released Under LGPL - original licence link has changed is not relivant.
20667  *
20668  * Fork - LGPL
20669  * <script type="text/javascript">
20670  */
20671  
20672
20673 /**
20674  * @class Roo.menu.ColorMenu
20675  * @extends Roo.menu.Menu
20676  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20677  * @constructor
20678  * Creates a new ColorMenu
20679  * @param {Object} config Configuration options
20680  */
20681 Roo.menu.ColorMenu = function(config){
20682     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20683     this.plain = true;
20684     var ci = new Roo.menu.ColorItem(config);
20685     this.add(ci);
20686     /**
20687      * The {@link Roo.ColorPalette} instance for this ColorMenu
20688      * @type ColorPalette
20689      */
20690     this.palette = ci.palette;
20691     /**
20692      * @event select
20693      * @param {ColorPalette} palette
20694      * @param {String} color
20695      */
20696     this.relayEvents(ci, ["select"]);
20697 };
20698 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20699  * Based on:
20700  * Ext JS Library 1.1.1
20701  * Copyright(c) 2006-2007, Ext JS, LLC.
20702  *
20703  * Originally Released Under LGPL - original licence link has changed is not relivant.
20704  *
20705  * Fork - LGPL
20706  * <script type="text/javascript">
20707  */
20708  
20709 /**
20710  * @class Roo.form.Field
20711  * @extends Roo.BoxComponent
20712  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20713  * @constructor
20714  * Creates a new Field
20715  * @param {Object} config Configuration options
20716  */
20717 Roo.form.Field = function(config){
20718     Roo.form.Field.superclass.constructor.call(this, config);
20719 };
20720
20721 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20722     /**
20723      * @cfg {String} fieldLabel Label to use when rendering a form.
20724      */
20725        /**
20726      * @cfg {String} qtip Mouse over tip
20727      */
20728      
20729     /**
20730      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20731      */
20732     invalidClass : "x-form-invalid",
20733     /**
20734      * @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")
20735      */
20736     invalidText : "The value in this field is invalid",
20737     /**
20738      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20739      */
20740     focusClass : "x-form-focus",
20741     /**
20742      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20743       automatic validation (defaults to "keyup").
20744      */
20745     validationEvent : "keyup",
20746     /**
20747      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20748      */
20749     validateOnBlur : true,
20750     /**
20751      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20752      */
20753     validationDelay : 250,
20754     /**
20755      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20756      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20757      */
20758     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20759     /**
20760      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20761      */
20762     fieldClass : "x-form-field",
20763     /**
20764      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20765      *<pre>
20766 Value         Description
20767 -----------   ----------------------------------------------------------------------
20768 qtip          Display a quick tip when the user hovers over the field
20769 title         Display a default browser title attribute popup
20770 under         Add a block div beneath the field containing the error text
20771 side          Add an error icon to the right of the field with a popup on hover
20772 [element id]  Add the error text directly to the innerHTML of the specified element
20773 </pre>
20774      */
20775     msgTarget : 'qtip',
20776     /**
20777      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20778      */
20779     msgFx : 'normal',
20780
20781     /**
20782      * @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.
20783      */
20784     readOnly : false,
20785
20786     /**
20787      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20788      */
20789     disabled : false,
20790
20791     /**
20792      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20793      */
20794     inputType : undefined,
20795     
20796     /**
20797      * @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).
20798          */
20799         tabIndex : undefined,
20800         
20801     // private
20802     isFormField : true,
20803
20804     // private
20805     hasFocus : false,
20806     /**
20807      * @property {Roo.Element} fieldEl
20808      * Element Containing the rendered Field (with label etc.)
20809      */
20810     /**
20811      * @cfg {Mixed} value A value to initialize this field with.
20812      */
20813     value : undefined,
20814
20815     /**
20816      * @cfg {String} name The field's HTML name attribute.
20817      */
20818     /**
20819      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20820      */
20821
20822         // private ??
20823         initComponent : function(){
20824         Roo.form.Field.superclass.initComponent.call(this);
20825         this.addEvents({
20826             /**
20827              * @event focus
20828              * Fires when this field receives input focus.
20829              * @param {Roo.form.Field} this
20830              */
20831             focus : true,
20832             /**
20833              * @event blur
20834              * Fires when this field loses input focus.
20835              * @param {Roo.form.Field} this
20836              */
20837             blur : true,
20838             /**
20839              * @event specialkey
20840              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20841              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20842              * @param {Roo.form.Field} this
20843              * @param {Roo.EventObject} e The event object
20844              */
20845             specialkey : true,
20846             /**
20847              * @event change
20848              * Fires just before the field blurs if the field value has changed.
20849              * @param {Roo.form.Field} this
20850              * @param {Mixed} newValue The new value
20851              * @param {Mixed} oldValue The original value
20852              */
20853             change : true,
20854             /**
20855              * @event invalid
20856              * Fires after the field has been marked as invalid.
20857              * @param {Roo.form.Field} this
20858              * @param {String} msg The validation message
20859              */
20860             invalid : true,
20861             /**
20862              * @event valid
20863              * Fires after the field has been validated with no errors.
20864              * @param {Roo.form.Field} this
20865              */
20866             valid : true,
20867              /**
20868              * @event keyup
20869              * Fires after the key up
20870              * @param {Roo.form.Field} this
20871              * @param {Roo.EventObject}  e The event Object
20872              */
20873             keyup : true
20874         });
20875     },
20876
20877     /**
20878      * Returns the name attribute of the field if available
20879      * @return {String} name The field name
20880      */
20881     getName: function(){
20882          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20883     },
20884
20885     // private
20886     onRender : function(ct, position){
20887         Roo.form.Field.superclass.onRender.call(this, ct, position);
20888         if(!this.el){
20889             var cfg = this.getAutoCreate();
20890             if(!cfg.name){
20891                 cfg.name = this.name || this.id;
20892             }
20893             if(this.inputType){
20894                 cfg.type = this.inputType;
20895             }
20896             this.el = ct.createChild(cfg, position);
20897         }
20898         var type = this.el.dom.type;
20899         if(type){
20900             if(type == 'password'){
20901                 type = 'text';
20902             }
20903             this.el.addClass('x-form-'+type);
20904         }
20905         if(this.readOnly){
20906             this.el.dom.readOnly = true;
20907         }
20908         if(this.tabIndex !== undefined){
20909             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20910         }
20911
20912         this.el.addClass([this.fieldClass, this.cls]);
20913         this.initValue();
20914     },
20915
20916     /**
20917      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20918      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20919      * @return {Roo.form.Field} this
20920      */
20921     applyTo : function(target){
20922         this.allowDomMove = false;
20923         this.el = Roo.get(target);
20924         this.render(this.el.dom.parentNode);
20925         return this;
20926     },
20927
20928     // private
20929     initValue : function(){
20930         if(this.value !== undefined){
20931             this.setValue(this.value);
20932         }else if(this.el.dom.value.length > 0){
20933             this.setValue(this.el.dom.value);
20934         }
20935     },
20936
20937     /**
20938      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20939      */
20940     isDirty : function() {
20941         if(this.disabled) {
20942             return false;
20943         }
20944         return String(this.getValue()) !== String(this.originalValue);
20945     },
20946
20947     // private
20948     afterRender : function(){
20949         Roo.form.Field.superclass.afterRender.call(this);
20950         this.initEvents();
20951     },
20952
20953     // private
20954     fireKey : function(e){
20955         //Roo.log('field ' + e.getKey());
20956         if(e.isNavKeyPress()){
20957             this.fireEvent("specialkey", this, e);
20958         }
20959     },
20960
20961     /**
20962      * Resets the current field value to the originally loaded value and clears any validation messages
20963      */
20964     reset : function(){
20965         this.setValue(this.originalValue);
20966         this.clearInvalid();
20967     },
20968
20969     // private
20970     initEvents : function(){
20971         // safari killled keypress - so keydown is now used..
20972         this.el.on("keydown" , this.fireKey,  this);
20973         this.el.on("focus", this.onFocus,  this);
20974         this.el.on("blur", this.onBlur,  this);
20975         this.el.relayEvent('keyup', this);
20976
20977         // reference to original value for reset
20978         this.originalValue = this.getValue();
20979     },
20980
20981     // private
20982     onFocus : function(){
20983         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20984             this.el.addClass(this.focusClass);
20985         }
20986         if(!this.hasFocus){
20987             this.hasFocus = true;
20988             this.startValue = this.getValue();
20989             this.fireEvent("focus", this);
20990         }
20991     },
20992
20993     beforeBlur : Roo.emptyFn,
20994
20995     // private
20996     onBlur : function(){
20997         this.beforeBlur();
20998         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20999             this.el.removeClass(this.focusClass);
21000         }
21001         this.hasFocus = false;
21002         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21003             this.validate();
21004         }
21005         var v = this.getValue();
21006         if(String(v) !== String(this.startValue)){
21007             this.fireEvent('change', this, v, this.startValue);
21008         }
21009         this.fireEvent("blur", this);
21010     },
21011
21012     /**
21013      * Returns whether or not the field value is currently valid
21014      * @param {Boolean} preventMark True to disable marking the field invalid
21015      * @return {Boolean} True if the value is valid, else false
21016      */
21017     isValid : function(preventMark){
21018         if(this.disabled){
21019             return true;
21020         }
21021         var restore = this.preventMark;
21022         this.preventMark = preventMark === true;
21023         var v = this.validateValue(this.processValue(this.getRawValue()));
21024         this.preventMark = restore;
21025         return v;
21026     },
21027
21028     /**
21029      * Validates the field value
21030      * @return {Boolean} True if the value is valid, else false
21031      */
21032     validate : function(){
21033         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21034             this.clearInvalid();
21035             return true;
21036         }
21037         return false;
21038     },
21039
21040     processValue : function(value){
21041         return value;
21042     },
21043
21044     // private
21045     // Subclasses should provide the validation implementation by overriding this
21046     validateValue : function(value){
21047         return true;
21048     },
21049
21050     /**
21051      * Mark this field as invalid
21052      * @param {String} msg The validation message
21053      */
21054     markInvalid : function(msg){
21055         if(!this.rendered || this.preventMark){ // not rendered
21056             return;
21057         }
21058         this.el.addClass(this.invalidClass);
21059         msg = msg || this.invalidText;
21060         switch(this.msgTarget){
21061             case 'qtip':
21062                 this.el.dom.qtip = msg;
21063                 this.el.dom.qclass = 'x-form-invalid-tip';
21064                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21065                     Roo.QuickTips.enable();
21066                 }
21067                 break;
21068             case 'title':
21069                 this.el.dom.title = msg;
21070                 break;
21071             case 'under':
21072                 if(!this.errorEl){
21073                     var elp = this.el.findParent('.x-form-element', 5, true);
21074                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21075                     this.errorEl.setWidth(elp.getWidth(true)-20);
21076                 }
21077                 this.errorEl.update(msg);
21078                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21079                 break;
21080             case 'side':
21081                 if(!this.errorIcon){
21082                     var elp = this.el.findParent('.x-form-element', 5, true);
21083                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21084                 }
21085                 this.alignErrorIcon();
21086                 this.errorIcon.dom.qtip = msg;
21087                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21088                 this.errorIcon.show();
21089                 this.on('resize', this.alignErrorIcon, this);
21090                 break;
21091             default:
21092                 var t = Roo.getDom(this.msgTarget);
21093                 t.innerHTML = msg;
21094                 t.style.display = this.msgDisplay;
21095                 break;
21096         }
21097         this.fireEvent('invalid', this, msg);
21098     },
21099
21100     // private
21101     alignErrorIcon : function(){
21102         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21103     },
21104
21105     /**
21106      * Clear any invalid styles/messages for this field
21107      */
21108     clearInvalid : function(){
21109         if(!this.rendered || this.preventMark){ // not rendered
21110             return;
21111         }
21112         this.el.removeClass(this.invalidClass);
21113         switch(this.msgTarget){
21114             case 'qtip':
21115                 this.el.dom.qtip = '';
21116                 break;
21117             case 'title':
21118                 this.el.dom.title = '';
21119                 break;
21120             case 'under':
21121                 if(this.errorEl){
21122                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21123                 }
21124                 break;
21125             case 'side':
21126                 if(this.errorIcon){
21127                     this.errorIcon.dom.qtip = '';
21128                     this.errorIcon.hide();
21129                     this.un('resize', this.alignErrorIcon, this);
21130                 }
21131                 break;
21132             default:
21133                 var t = Roo.getDom(this.msgTarget);
21134                 t.innerHTML = '';
21135                 t.style.display = 'none';
21136                 break;
21137         }
21138         this.fireEvent('valid', this);
21139     },
21140
21141     /**
21142      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21143      * @return {Mixed} value The field value
21144      */
21145     getRawValue : function(){
21146         var v = this.el.getValue();
21147         if(v === this.emptyText){
21148             v = '';
21149         }
21150         return v;
21151     },
21152
21153     /**
21154      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21155      * @return {Mixed} value The field value
21156      */
21157     getValue : function(){
21158         var v = this.el.getValue();
21159         if(v === this.emptyText || v === undefined){
21160             v = '';
21161         }
21162         return v;
21163     },
21164
21165     /**
21166      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21167      * @param {Mixed} value The value to set
21168      */
21169     setRawValue : function(v){
21170         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21171     },
21172
21173     /**
21174      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21175      * @param {Mixed} value The value to set
21176      */
21177     setValue : function(v){
21178         this.value = v;
21179         if(this.rendered){
21180             this.el.dom.value = (v === null || v === undefined ? '' : v);
21181             this.validate();
21182         }
21183     },
21184
21185     adjustSize : function(w, h){
21186         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21187         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21188         return s;
21189     },
21190
21191     adjustWidth : function(tag, w){
21192         tag = tag.toLowerCase();
21193         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21194             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21195                 if(tag == 'input'){
21196                     return w + 2;
21197                 }
21198                 if(tag = 'textarea'){
21199                     return w-2;
21200                 }
21201             }else if(Roo.isOpera){
21202                 if(tag == 'input'){
21203                     return w + 2;
21204                 }
21205                 if(tag = 'textarea'){
21206                     return w-2;
21207                 }
21208             }
21209         }
21210         return w;
21211     }
21212 });
21213
21214
21215 // anything other than normal should be considered experimental
21216 Roo.form.Field.msgFx = {
21217     normal : {
21218         show: function(msgEl, f){
21219             msgEl.setDisplayed('block');
21220         },
21221
21222         hide : function(msgEl, f){
21223             msgEl.setDisplayed(false).update('');
21224         }
21225     },
21226
21227     slide : {
21228         show: function(msgEl, f){
21229             msgEl.slideIn('t', {stopFx:true});
21230         },
21231
21232         hide : function(msgEl, f){
21233             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21234         }
21235     },
21236
21237     slideRight : {
21238         show: function(msgEl, f){
21239             msgEl.fixDisplay();
21240             msgEl.alignTo(f.el, 'tl-tr');
21241             msgEl.slideIn('l', {stopFx:true});
21242         },
21243
21244         hide : function(msgEl, f){
21245             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21246         }
21247     }
21248 };/*
21249  * Based on:
21250  * Ext JS Library 1.1.1
21251  * Copyright(c) 2006-2007, Ext JS, LLC.
21252  *
21253  * Originally Released Under LGPL - original licence link has changed is not relivant.
21254  *
21255  * Fork - LGPL
21256  * <script type="text/javascript">
21257  */
21258  
21259
21260 /**
21261  * @class Roo.form.TextField
21262  * @extends Roo.form.Field
21263  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21264  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21265  * @constructor
21266  * Creates a new TextField
21267  * @param {Object} config Configuration options
21268  */
21269 Roo.form.TextField = function(config){
21270     Roo.form.TextField.superclass.constructor.call(this, config);
21271     this.addEvents({
21272         /**
21273          * @event autosize
21274          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21275          * according to the default logic, but this event provides a hook for the developer to apply additional
21276          * logic at runtime to resize the field if needed.
21277              * @param {Roo.form.Field} this This text field
21278              * @param {Number} width The new field width
21279              */
21280         autosize : true
21281     });
21282 };
21283
21284 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21285     /**
21286      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21287      */
21288     grow : false,
21289     /**
21290      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21291      */
21292     growMin : 30,
21293     /**
21294      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21295      */
21296     growMax : 800,
21297     /**
21298      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21299      */
21300     vtype : null,
21301     /**
21302      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21303      */
21304     maskRe : null,
21305     /**
21306      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21307      */
21308     disableKeyFilter : false,
21309     /**
21310      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21311      */
21312     allowBlank : true,
21313     /**
21314      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21315      */
21316     minLength : 0,
21317     /**
21318      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21319      */
21320     maxLength : Number.MAX_VALUE,
21321     /**
21322      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21323      */
21324     minLengthText : "The minimum length for this field is {0}",
21325     /**
21326      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21327      */
21328     maxLengthText : "The maximum length for this field is {0}",
21329     /**
21330      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21331      */
21332     selectOnFocus : false,
21333     /**
21334      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21335      */
21336     blankText : "This field is required",
21337     /**
21338      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21339      * If available, this function will be called only after the basic validators all return true, and will be passed the
21340      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21341      */
21342     validator : null,
21343     /**
21344      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21345      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21346      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21347      */
21348     regex : null,
21349     /**
21350      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21351      */
21352     regexText : "",
21353     /**
21354      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21355      */
21356     emptyText : null,
21357     /**
21358      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21359      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21360      */
21361     emptyClass : 'x-form-empty-field',
21362
21363     // private
21364     initEvents : function(){
21365         Roo.form.TextField.superclass.initEvents.call(this);
21366         if(this.validationEvent == 'keyup'){
21367             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21368             this.el.on('keyup', this.filterValidation, this);
21369         }
21370         else if(this.validationEvent !== false){
21371             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21372         }
21373         if(this.selectOnFocus || this.emptyText){
21374             this.on("focus", this.preFocus, this);
21375             if(this.emptyText){
21376                 this.on('blur', this.postBlur, this);
21377                 this.applyEmptyText();
21378             }
21379         }
21380         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21381             this.el.on("keypress", this.filterKeys, this);
21382         }
21383         if(this.grow){
21384             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21385             this.el.on("click", this.autoSize,  this);
21386         }
21387     },
21388
21389     processValue : function(value){
21390         if(this.stripCharsRe){
21391             var newValue = value.replace(this.stripCharsRe, '');
21392             if(newValue !== value){
21393                 this.setRawValue(newValue);
21394                 return newValue;
21395             }
21396         }
21397         return value;
21398     },
21399
21400     filterValidation : function(e){
21401         if(!e.isNavKeyPress()){
21402             this.validationTask.delay(this.validationDelay);
21403         }
21404     },
21405
21406     // private
21407     onKeyUp : function(e){
21408         if(!e.isNavKeyPress()){
21409             this.autoSize();
21410         }
21411     },
21412
21413     /**
21414      * Resets the current field value to the originally-loaded value and clears any validation messages.
21415      * Also adds emptyText and emptyClass if the original value was blank.
21416      */
21417     reset : function(){
21418         Roo.form.TextField.superclass.reset.call(this);
21419         this.applyEmptyText();
21420     },
21421
21422     applyEmptyText : function(){
21423         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21424             this.setRawValue(this.emptyText);
21425             this.el.addClass(this.emptyClass);
21426         }
21427     },
21428
21429     // private
21430     preFocus : function(){
21431         if(this.emptyText){
21432             if(this.el.dom.value == this.emptyText){
21433                 this.setRawValue('');
21434             }
21435             this.el.removeClass(this.emptyClass);
21436         }
21437         if(this.selectOnFocus){
21438             this.el.dom.select();
21439         }
21440     },
21441
21442     // private
21443     postBlur : function(){
21444         this.applyEmptyText();
21445     },
21446
21447     // private
21448     filterKeys : function(e){
21449         var k = e.getKey();
21450         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21451             return;
21452         }
21453         var c = e.getCharCode(), cc = String.fromCharCode(c);
21454         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21455             return;
21456         }
21457         if(!this.maskRe.test(cc)){
21458             e.stopEvent();
21459         }
21460     },
21461
21462     setValue : function(v){
21463         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21464             this.el.removeClass(this.emptyClass);
21465         }
21466         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21467         this.applyEmptyText();
21468         this.autoSize();
21469     },
21470
21471     /**
21472      * Validates a value according to the field's validation rules and marks the field as invalid
21473      * if the validation fails
21474      * @param {Mixed} value The value to validate
21475      * @return {Boolean} True if the value is valid, else false
21476      */
21477     validateValue : function(value){
21478         if(value.length < 1 || value === this.emptyText){ // if it's blank
21479              if(this.allowBlank){
21480                 this.clearInvalid();
21481                 return true;
21482              }else{
21483                 this.markInvalid(this.blankText);
21484                 return false;
21485              }
21486         }
21487         if(value.length < this.minLength){
21488             this.markInvalid(String.format(this.minLengthText, this.minLength));
21489             return false;
21490         }
21491         if(value.length > this.maxLength){
21492             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21493             return false;
21494         }
21495         if(this.vtype){
21496             var vt = Roo.form.VTypes;
21497             if(!vt[this.vtype](value, this)){
21498                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21499                 return false;
21500             }
21501         }
21502         if(typeof this.validator == "function"){
21503             var msg = this.validator(value);
21504             if(msg !== true){
21505                 this.markInvalid(msg);
21506                 return false;
21507             }
21508         }
21509         if(this.regex && !this.regex.test(value)){
21510             this.markInvalid(this.regexText);
21511             return false;
21512         }
21513         return true;
21514     },
21515
21516     /**
21517      * Selects text in this field
21518      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21519      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21520      */
21521     selectText : function(start, end){
21522         var v = this.getRawValue();
21523         if(v.length > 0){
21524             start = start === undefined ? 0 : start;
21525             end = end === undefined ? v.length : end;
21526             var d = this.el.dom;
21527             if(d.setSelectionRange){
21528                 d.setSelectionRange(start, end);
21529             }else if(d.createTextRange){
21530                 var range = d.createTextRange();
21531                 range.moveStart("character", start);
21532                 range.moveEnd("character", v.length-end);
21533                 range.select();
21534             }
21535         }
21536     },
21537
21538     /**
21539      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21540      * This only takes effect if grow = true, and fires the autosize event.
21541      */
21542     autoSize : function(){
21543         if(!this.grow || !this.rendered){
21544             return;
21545         }
21546         if(!this.metrics){
21547             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21548         }
21549         var el = this.el;
21550         var v = el.dom.value;
21551         var d = document.createElement('div');
21552         d.appendChild(document.createTextNode(v));
21553         v = d.innerHTML;
21554         d = null;
21555         v += "&#160;";
21556         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21557         this.el.setWidth(w);
21558         this.fireEvent("autosize", this, w);
21559     }
21560 });/*
21561  * Based on:
21562  * Ext JS Library 1.1.1
21563  * Copyright(c) 2006-2007, Ext JS, LLC.
21564  *
21565  * Originally Released Under LGPL - original licence link has changed is not relivant.
21566  *
21567  * Fork - LGPL
21568  * <script type="text/javascript">
21569  */
21570  
21571 /**
21572  * @class Roo.form.Hidden
21573  * @extends Roo.form.TextField
21574  * Simple Hidden element used on forms 
21575  * 
21576  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21577  * 
21578  * @constructor
21579  * Creates a new Hidden form element.
21580  * @param {Object} config Configuration options
21581  */
21582
21583
21584
21585 // easy hidden field...
21586 Roo.form.Hidden = function(config){
21587     Roo.form.Hidden.superclass.constructor.call(this, config);
21588 };
21589   
21590 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21591     fieldLabel:      '',
21592     inputType:      'hidden',
21593     width:          50,
21594     allowBlank:     true,
21595     labelSeparator: '',
21596     hidden:         true,
21597     itemCls :       'x-form-item-display-none'
21598
21599
21600 });
21601
21602
21603 /*
21604  * Based on:
21605  * Ext JS Library 1.1.1
21606  * Copyright(c) 2006-2007, Ext JS, LLC.
21607  *
21608  * Originally Released Under LGPL - original licence link has changed is not relivant.
21609  *
21610  * Fork - LGPL
21611  * <script type="text/javascript">
21612  */
21613  
21614 /**
21615  * @class Roo.form.TriggerField
21616  * @extends Roo.form.TextField
21617  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21618  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21619  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21620  * for which you can provide a custom implementation.  For example:
21621  * <pre><code>
21622 var trigger = new Roo.form.TriggerField();
21623 trigger.onTriggerClick = myTriggerFn;
21624 trigger.applyTo('my-field');
21625 </code></pre>
21626  *
21627  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21628  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21629  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21630  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21631  * @constructor
21632  * Create a new TriggerField.
21633  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21634  * to the base TextField)
21635  */
21636 Roo.form.TriggerField = function(config){
21637     this.mimicing = false;
21638     Roo.form.TriggerField.superclass.constructor.call(this, config);
21639 };
21640
21641 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21642     /**
21643      * @cfg {String} triggerClass A CSS class to apply to the trigger
21644      */
21645     /**
21646      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21647      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21648      */
21649     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21650     /**
21651      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21652      */
21653     hideTrigger:false,
21654
21655     /** @cfg {Boolean} grow @hide */
21656     /** @cfg {Number} growMin @hide */
21657     /** @cfg {Number} growMax @hide */
21658
21659     /**
21660      * @hide 
21661      * @method
21662      */
21663     autoSize: Roo.emptyFn,
21664     // private
21665     monitorTab : true,
21666     // private
21667     deferHeight : true,
21668
21669     
21670     actionMode : 'wrap',
21671     // private
21672     onResize : function(w, h){
21673         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21674         if(typeof w == 'number'){
21675             var x = w - this.trigger.getWidth();
21676             this.el.setWidth(this.adjustWidth('input', x));
21677             this.trigger.setStyle('left', x+'px');
21678         }
21679     },
21680
21681     // private
21682     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21683
21684     // private
21685     getResizeEl : function(){
21686         return this.wrap;
21687     },
21688
21689     // private
21690     getPositionEl : function(){
21691         return this.wrap;
21692     },
21693
21694     // private
21695     alignErrorIcon : function(){
21696         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21697     },
21698
21699     // private
21700     onRender : function(ct, position){
21701         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21702         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21703         this.trigger = this.wrap.createChild(this.triggerConfig ||
21704                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21705         if(this.hideTrigger){
21706             this.trigger.setDisplayed(false);
21707         }
21708         this.initTrigger();
21709         if(!this.width){
21710             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21711         }
21712     },
21713
21714     // private
21715     initTrigger : function(){
21716         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21717         this.trigger.addClassOnOver('x-form-trigger-over');
21718         this.trigger.addClassOnClick('x-form-trigger-click');
21719     },
21720
21721     // private
21722     onDestroy : function(){
21723         if(this.trigger){
21724             this.trigger.removeAllListeners();
21725             this.trigger.remove();
21726         }
21727         if(this.wrap){
21728             this.wrap.remove();
21729         }
21730         Roo.form.TriggerField.superclass.onDestroy.call(this);
21731     },
21732
21733     // private
21734     onFocus : function(){
21735         Roo.form.TriggerField.superclass.onFocus.call(this);
21736         if(!this.mimicing){
21737             this.wrap.addClass('x-trigger-wrap-focus');
21738             this.mimicing = true;
21739             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21740             if(this.monitorTab){
21741                 this.el.on("keydown", this.checkTab, this);
21742             }
21743         }
21744     },
21745
21746     // private
21747     checkTab : function(e){
21748         if(e.getKey() == e.TAB){
21749             this.triggerBlur();
21750         }
21751     },
21752
21753     // private
21754     onBlur : function(){
21755         // do nothing
21756     },
21757
21758     // private
21759     mimicBlur : function(e, t){
21760         if(!this.wrap.contains(t) && this.validateBlur()){
21761             this.triggerBlur();
21762         }
21763     },
21764
21765     // private
21766     triggerBlur : function(){
21767         this.mimicing = false;
21768         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21769         if(this.monitorTab){
21770             this.el.un("keydown", this.checkTab, this);
21771         }
21772         this.wrap.removeClass('x-trigger-wrap-focus');
21773         Roo.form.TriggerField.superclass.onBlur.call(this);
21774     },
21775
21776     // private
21777     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21778     validateBlur : function(e, t){
21779         return true;
21780     },
21781
21782     // private
21783     onDisable : function(){
21784         Roo.form.TriggerField.superclass.onDisable.call(this);
21785         if(this.wrap){
21786             this.wrap.addClass('x-item-disabled');
21787         }
21788     },
21789
21790     // private
21791     onEnable : function(){
21792         Roo.form.TriggerField.superclass.onEnable.call(this);
21793         if(this.wrap){
21794             this.wrap.removeClass('x-item-disabled');
21795         }
21796     },
21797
21798     // private
21799     onShow : function(){
21800         var ae = this.getActionEl();
21801         
21802         if(ae){
21803             ae.dom.style.display = '';
21804             ae.dom.style.visibility = 'visible';
21805         }
21806     },
21807
21808     // private
21809     
21810     onHide : function(){
21811         var ae = this.getActionEl();
21812         ae.dom.style.display = 'none';
21813     },
21814
21815     /**
21816      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21817      * by an implementing function.
21818      * @method
21819      * @param {EventObject} e
21820      */
21821     onTriggerClick : Roo.emptyFn
21822 });
21823
21824 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21825 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21826 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21827 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21828     initComponent : function(){
21829         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21830
21831         this.triggerConfig = {
21832             tag:'span', cls:'x-form-twin-triggers', cn:[
21833             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21834             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21835         ]};
21836     },
21837
21838     getTrigger : function(index){
21839         return this.triggers[index];
21840     },
21841
21842     initTrigger : function(){
21843         var ts = this.trigger.select('.x-form-trigger', true);
21844         this.wrap.setStyle('overflow', 'hidden');
21845         var triggerField = this;
21846         ts.each(function(t, all, index){
21847             t.hide = function(){
21848                 var w = triggerField.wrap.getWidth();
21849                 this.dom.style.display = 'none';
21850                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21851             };
21852             t.show = function(){
21853                 var w = triggerField.wrap.getWidth();
21854                 this.dom.style.display = '';
21855                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21856             };
21857             var triggerIndex = 'Trigger'+(index+1);
21858
21859             if(this['hide'+triggerIndex]){
21860                 t.dom.style.display = 'none';
21861             }
21862             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21863             t.addClassOnOver('x-form-trigger-over');
21864             t.addClassOnClick('x-form-trigger-click');
21865         }, this);
21866         this.triggers = ts.elements;
21867     },
21868
21869     onTrigger1Click : Roo.emptyFn,
21870     onTrigger2Click : Roo.emptyFn
21871 });/*
21872  * Based on:
21873  * Ext JS Library 1.1.1
21874  * Copyright(c) 2006-2007, Ext JS, LLC.
21875  *
21876  * Originally Released Under LGPL - original licence link has changed is not relivant.
21877  *
21878  * Fork - LGPL
21879  * <script type="text/javascript">
21880  */
21881  
21882 /**
21883  * @class Roo.form.TextArea
21884  * @extends Roo.form.TextField
21885  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21886  * support for auto-sizing.
21887  * @constructor
21888  * Creates a new TextArea
21889  * @param {Object} config Configuration options
21890  */
21891 Roo.form.TextArea = function(config){
21892     Roo.form.TextArea.superclass.constructor.call(this, config);
21893     // these are provided exchanges for backwards compat
21894     // minHeight/maxHeight were replaced by growMin/growMax to be
21895     // compatible with TextField growing config values
21896     if(this.minHeight !== undefined){
21897         this.growMin = this.minHeight;
21898     }
21899     if(this.maxHeight !== undefined){
21900         this.growMax = this.maxHeight;
21901     }
21902 };
21903
21904 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21905     /**
21906      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21907      */
21908     growMin : 60,
21909     /**
21910      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21911      */
21912     growMax: 1000,
21913     /**
21914      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21915      * in the field (equivalent to setting overflow: hidden, defaults to false)
21916      */
21917     preventScrollbars: false,
21918     /**
21919      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21920      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21921      */
21922
21923     // private
21924     onRender : function(ct, position){
21925         if(!this.el){
21926             this.defaultAutoCreate = {
21927                 tag: "textarea",
21928                 style:"width:300px;height:60px;",
21929                 autocomplete: "off"
21930             };
21931         }
21932         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21933         if(this.grow){
21934             this.textSizeEl = Roo.DomHelper.append(document.body, {
21935                 tag: "pre", cls: "x-form-grow-sizer"
21936             });
21937             if(this.preventScrollbars){
21938                 this.el.setStyle("overflow", "hidden");
21939             }
21940             this.el.setHeight(this.growMin);
21941         }
21942     },
21943
21944     onDestroy : function(){
21945         if(this.textSizeEl){
21946             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21947         }
21948         Roo.form.TextArea.superclass.onDestroy.call(this);
21949     },
21950
21951     // private
21952     onKeyUp : function(e){
21953         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21954             this.autoSize();
21955         }
21956     },
21957
21958     /**
21959      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21960      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21961      */
21962     autoSize : function(){
21963         if(!this.grow || !this.textSizeEl){
21964             return;
21965         }
21966         var el = this.el;
21967         var v = el.dom.value;
21968         var ts = this.textSizeEl;
21969
21970         ts.innerHTML = '';
21971         ts.appendChild(document.createTextNode(v));
21972         v = ts.innerHTML;
21973
21974         Roo.fly(ts).setWidth(this.el.getWidth());
21975         if(v.length < 1){
21976             v = "&#160;&#160;";
21977         }else{
21978             if(Roo.isIE){
21979                 v = v.replace(/\n/g, '<p>&#160;</p>');
21980             }
21981             v += "&#160;\n&#160;";
21982         }
21983         ts.innerHTML = v;
21984         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21985         if(h != this.lastHeight){
21986             this.lastHeight = h;
21987             this.el.setHeight(h);
21988             this.fireEvent("autosize", this, h);
21989         }
21990     }
21991 });/*
21992  * Based on:
21993  * Ext JS Library 1.1.1
21994  * Copyright(c) 2006-2007, Ext JS, LLC.
21995  *
21996  * Originally Released Under LGPL - original licence link has changed is not relivant.
21997  *
21998  * Fork - LGPL
21999  * <script type="text/javascript">
22000  */
22001  
22002
22003 /**
22004  * @class Roo.form.NumberField
22005  * @extends Roo.form.TextField
22006  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22007  * @constructor
22008  * Creates a new NumberField
22009  * @param {Object} config Configuration options
22010  */
22011 Roo.form.NumberField = function(config){
22012     Roo.form.NumberField.superclass.constructor.call(this, config);
22013 };
22014
22015 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22016     /**
22017      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22018      */
22019     fieldClass: "x-form-field x-form-num-field",
22020     /**
22021      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22022      */
22023     allowDecimals : true,
22024     /**
22025      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22026      */
22027     decimalSeparator : ".",
22028     /**
22029      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22030      */
22031     decimalPrecision : 2,
22032     /**
22033      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22034      */
22035     allowNegative : true,
22036     /**
22037      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22038      */
22039     minValue : Number.NEGATIVE_INFINITY,
22040     /**
22041      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22042      */
22043     maxValue : Number.MAX_VALUE,
22044     /**
22045      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22046      */
22047     minText : "The minimum value for this field is {0}",
22048     /**
22049      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22050      */
22051     maxText : "The maximum value for this field is {0}",
22052     /**
22053      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22054      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22055      */
22056     nanText : "{0} is not a valid number",
22057
22058     // private
22059     initEvents : function(){
22060         Roo.form.NumberField.superclass.initEvents.call(this);
22061         var allowed = "0123456789";
22062         if(this.allowDecimals){
22063             allowed += this.decimalSeparator;
22064         }
22065         if(this.allowNegative){
22066             allowed += "-";
22067         }
22068         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22069         var keyPress = function(e){
22070             var k = e.getKey();
22071             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22072                 return;
22073             }
22074             var c = e.getCharCode();
22075             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22076                 e.stopEvent();
22077             }
22078         };
22079         this.el.on("keypress", keyPress, this);
22080     },
22081
22082     // private
22083     validateValue : function(value){
22084         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22085             return false;
22086         }
22087         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22088              return true;
22089         }
22090         var num = this.parseValue(value);
22091         if(isNaN(num)){
22092             this.markInvalid(String.format(this.nanText, value));
22093             return false;
22094         }
22095         if(num < this.minValue){
22096             this.markInvalid(String.format(this.minText, this.minValue));
22097             return false;
22098         }
22099         if(num > this.maxValue){
22100             this.markInvalid(String.format(this.maxText, this.maxValue));
22101             return false;
22102         }
22103         return true;
22104     },
22105
22106     getValue : function(){
22107         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22108     },
22109
22110     // private
22111     parseValue : function(value){
22112         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22113         return isNaN(value) ? '' : value;
22114     },
22115
22116     // private
22117     fixPrecision : function(value){
22118         var nan = isNaN(value);
22119         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22120             return nan ? '' : value;
22121         }
22122         return parseFloat(value).toFixed(this.decimalPrecision);
22123     },
22124
22125     setValue : function(v){
22126         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22127     },
22128
22129     // private
22130     decimalPrecisionFcn : function(v){
22131         return Math.floor(v);
22132     },
22133
22134     beforeBlur : function(){
22135         var v = this.parseValue(this.getRawValue());
22136         if(v){
22137             this.setValue(this.fixPrecision(v));
22138         }
22139     }
22140 });/*
22141  * Based on:
22142  * Ext JS Library 1.1.1
22143  * Copyright(c) 2006-2007, Ext JS, LLC.
22144  *
22145  * Originally Released Under LGPL - original licence link has changed is not relivant.
22146  *
22147  * Fork - LGPL
22148  * <script type="text/javascript">
22149  */
22150  
22151 /**
22152  * @class Roo.form.DateField
22153  * @extends Roo.form.TriggerField
22154  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22155 * @constructor
22156 * Create a new DateField
22157 * @param {Object} config
22158  */
22159 Roo.form.DateField = function(config){
22160     Roo.form.DateField.superclass.constructor.call(this, config);
22161     
22162       this.addEvents({
22163          
22164         /**
22165          * @event select
22166          * Fires when a date is selected
22167              * @param {Roo.form.DateField} combo This combo box
22168              * @param {Date} date The date selected
22169              */
22170         'select' : true
22171          
22172     });
22173     
22174     
22175     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22176     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22177     this.ddMatch = null;
22178     if(this.disabledDates){
22179         var dd = this.disabledDates;
22180         var re = "(?:";
22181         for(var i = 0; i < dd.length; i++){
22182             re += dd[i];
22183             if(i != dd.length-1) re += "|";
22184         }
22185         this.ddMatch = new RegExp(re + ")");
22186     }
22187 };
22188
22189 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22190     /**
22191      * @cfg {String} format
22192      * The default date format string which can be overriden for localization support.  The format must be
22193      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22194      */
22195     format : "m/d/y",
22196     /**
22197      * @cfg {String} altFormats
22198      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22199      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22200      */
22201     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22202     /**
22203      * @cfg {Array} disabledDays
22204      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22205      */
22206     disabledDays : null,
22207     /**
22208      * @cfg {String} disabledDaysText
22209      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22210      */
22211     disabledDaysText : "Disabled",
22212     /**
22213      * @cfg {Array} disabledDates
22214      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22215      * expression so they are very powerful. Some examples:
22216      * <ul>
22217      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22218      * <li>["03/08", "09/16"] would disable those days for every year</li>
22219      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22220      * <li>["03/../2006"] would disable every day in March 2006</li>
22221      * <li>["^03"] would disable every day in every March</li>
22222      * </ul>
22223      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22224      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22225      */
22226     disabledDates : null,
22227     /**
22228      * @cfg {String} disabledDatesText
22229      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22230      */
22231     disabledDatesText : "Disabled",
22232     /**
22233      * @cfg {Date/String} minValue
22234      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22235      * valid format (defaults to null).
22236      */
22237     minValue : null,
22238     /**
22239      * @cfg {Date/String} maxValue
22240      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22241      * valid format (defaults to null).
22242      */
22243     maxValue : null,
22244     /**
22245      * @cfg {String} minText
22246      * The error text to display when the date in the cell is before minValue (defaults to
22247      * 'The date in this field must be after {minValue}').
22248      */
22249     minText : "The date in this field must be equal to or after {0}",
22250     /**
22251      * @cfg {String} maxText
22252      * The error text to display when the date in the cell is after maxValue (defaults to
22253      * 'The date in this field must be before {maxValue}').
22254      */
22255     maxText : "The date in this field must be equal to or before {0}",
22256     /**
22257      * @cfg {String} invalidText
22258      * The error text to display when the date in the field is invalid (defaults to
22259      * '{value} is not a valid date - it must be in the format {format}').
22260      */
22261     invalidText : "{0} is not a valid date - it must be in the format {1}",
22262     /**
22263      * @cfg {String} triggerClass
22264      * An additional CSS class used to style the trigger button.  The trigger will always get the
22265      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22266      * which displays a calendar icon).
22267      */
22268     triggerClass : 'x-form-date-trigger',
22269     
22270
22271     /**
22272      * @cfg {bool} useIso
22273      * if enabled, then the date field will use a hidden field to store the 
22274      * real value as iso formated date. default (false)
22275      */ 
22276     useIso : false,
22277     /**
22278      * @cfg {String/Object} autoCreate
22279      * A DomHelper element spec, or true for a default element spec (defaults to
22280      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22281      */ 
22282     // private
22283     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22284     
22285     // private
22286     hiddenField: false,
22287     
22288     onRender : function(ct, position)
22289     {
22290         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22291         if (this.useIso) {
22292             this.el.dom.removeAttribute('name'); 
22293             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22294                     'before', true);
22295             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22296             // prevent input submission
22297             this.hiddenName = this.name;
22298         }
22299             
22300             
22301     },
22302     
22303     // private
22304     validateValue : function(value)
22305     {
22306         value = this.formatDate(value);
22307         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22308             return false;
22309         }
22310         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22311              return true;
22312         }
22313         var svalue = value;
22314         value = this.parseDate(value);
22315         if(!value){
22316             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22317             return false;
22318         }
22319         var time = value.getTime();
22320         if(this.minValue && time < this.minValue.getTime()){
22321             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22322             return false;
22323         }
22324         if(this.maxValue && time > this.maxValue.getTime()){
22325             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22326             return false;
22327         }
22328         if(this.disabledDays){
22329             var day = value.getDay();
22330             for(var i = 0; i < this.disabledDays.length; i++) {
22331                 if(day === this.disabledDays[i]){
22332                     this.markInvalid(this.disabledDaysText);
22333                     return false;
22334                 }
22335             }
22336         }
22337         var fvalue = this.formatDate(value);
22338         if(this.ddMatch && this.ddMatch.test(fvalue)){
22339             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22340             return false;
22341         }
22342         return true;
22343     },
22344
22345     // private
22346     // Provides logic to override the default TriggerField.validateBlur which just returns true
22347     validateBlur : function(){
22348         return !this.menu || !this.menu.isVisible();
22349     },
22350
22351     /**
22352      * Returns the current date value of the date field.
22353      * @return {Date} The date value
22354      */
22355     getValue : function(){
22356         
22357         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22358     },
22359
22360     /**
22361      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22362      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22363      * (the default format used is "m/d/y").
22364      * <br />Usage:
22365      * <pre><code>
22366 //All of these calls set the same date value (May 4, 2006)
22367
22368 //Pass a date object:
22369 var dt = new Date('5/4/06');
22370 dateField.setValue(dt);
22371
22372 //Pass a date string (default format):
22373 dateField.setValue('5/4/06');
22374
22375 //Pass a date string (custom format):
22376 dateField.format = 'Y-m-d';
22377 dateField.setValue('2006-5-4');
22378 </code></pre>
22379      * @param {String/Date} date The date or valid date string
22380      */
22381     setValue : function(date){
22382         if (this.hiddenField) {
22383             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22384         }
22385         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22386     },
22387
22388     // private
22389     parseDate : function(value){
22390         if(!value || value instanceof Date){
22391             return value;
22392         }
22393         var v = Date.parseDate(value, this.format);
22394         if(!v && this.altFormats){
22395             if(!this.altFormatsArray){
22396                 this.altFormatsArray = this.altFormats.split("|");
22397             }
22398             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22399                 v = Date.parseDate(value, this.altFormatsArray[i]);
22400             }
22401         }
22402         return v;
22403     },
22404
22405     // private
22406     formatDate : function(date, fmt){
22407         return (!date || !(date instanceof Date)) ?
22408                date : date.dateFormat(fmt || this.format);
22409     },
22410
22411     // private
22412     menuListeners : {
22413         select: function(m, d){
22414             this.setValue(d);
22415             this.fireEvent('select', this, d);
22416         },
22417         show : function(){ // retain focus styling
22418             this.onFocus();
22419         },
22420         hide : function(){
22421             this.focus.defer(10, this);
22422             var ml = this.menuListeners;
22423             this.menu.un("select", ml.select,  this);
22424             this.menu.un("show", ml.show,  this);
22425             this.menu.un("hide", ml.hide,  this);
22426         }
22427     },
22428
22429     // private
22430     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22431     onTriggerClick : function(){
22432         if(this.disabled){
22433             return;
22434         }
22435         if(this.menu == null){
22436             this.menu = new Roo.menu.DateMenu();
22437         }
22438         Roo.apply(this.menu.picker,  {
22439             showClear: this.allowBlank,
22440             minDate : this.minValue,
22441             maxDate : this.maxValue,
22442             disabledDatesRE : this.ddMatch,
22443             disabledDatesText : this.disabledDatesText,
22444             disabledDays : this.disabledDays,
22445             disabledDaysText : this.disabledDaysText,
22446             format : this.format,
22447             minText : String.format(this.minText, this.formatDate(this.minValue)),
22448             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22449         });
22450         this.menu.on(Roo.apply({}, this.menuListeners, {
22451             scope:this
22452         }));
22453         this.menu.picker.setValue(this.getValue() || new Date());
22454         this.menu.show(this.el, "tl-bl?");
22455     },
22456
22457     beforeBlur : function(){
22458         var v = this.parseDate(this.getRawValue());
22459         if(v){
22460             this.setValue(v);
22461         }
22462     }
22463
22464     /** @cfg {Boolean} grow @hide */
22465     /** @cfg {Number} growMin @hide */
22466     /** @cfg {Number} growMax @hide */
22467     /**
22468      * @hide
22469      * @method autoSize
22470      */
22471 });/*
22472  * Based on:
22473  * Ext JS Library 1.1.1
22474  * Copyright(c) 2006-2007, Ext JS, LLC.
22475  *
22476  * Originally Released Under LGPL - original licence link has changed is not relivant.
22477  *
22478  * Fork - LGPL
22479  * <script type="text/javascript">
22480  */
22481  
22482
22483 /**
22484  * @class Roo.form.ComboBox
22485  * @extends Roo.form.TriggerField
22486  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22487  * @constructor
22488  * Create a new ComboBox.
22489  * @param {Object} config Configuration options
22490  */
22491 Roo.form.ComboBox = function(config){
22492     Roo.form.ComboBox.superclass.constructor.call(this, config);
22493     this.addEvents({
22494         /**
22495          * @event expand
22496          * Fires when the dropdown list is expanded
22497              * @param {Roo.form.ComboBox} combo This combo box
22498              */
22499         'expand' : true,
22500         /**
22501          * @event collapse
22502          * Fires when the dropdown list is collapsed
22503              * @param {Roo.form.ComboBox} combo This combo box
22504              */
22505         'collapse' : true,
22506         /**
22507          * @event beforeselect
22508          * Fires before a list item is selected. Return false to cancel the selection.
22509              * @param {Roo.form.ComboBox} combo This combo box
22510              * @param {Roo.data.Record} record The data record returned from the underlying store
22511              * @param {Number} index The index of the selected item in the dropdown list
22512              */
22513         'beforeselect' : true,
22514         /**
22515          * @event select
22516          * Fires when a list item is selected
22517              * @param {Roo.form.ComboBox} combo This combo box
22518              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22519              * @param {Number} index The index of the selected item in the dropdown list
22520              */
22521         'select' : true,
22522         /**
22523          * @event beforequery
22524          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22525          * The event object passed has these properties:
22526              * @param {Roo.form.ComboBox} combo This combo box
22527              * @param {String} query The query
22528              * @param {Boolean} forceAll true to force "all" query
22529              * @param {Boolean} cancel true to cancel the query
22530              * @param {Object} e The query event object
22531              */
22532         'beforequery': true,
22533          /**
22534          * @event add
22535          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22536              * @param {Roo.form.ComboBox} combo This combo box
22537              */
22538         'add' : true,
22539         /**
22540          * @event edit
22541          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22542              * @param {Roo.form.ComboBox} combo This combo box
22543              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22544              */
22545         'edit' : true
22546         
22547         
22548     });
22549     if(this.transform){
22550         this.allowDomMove = false;
22551         var s = Roo.getDom(this.transform);
22552         if(!this.hiddenName){
22553             this.hiddenName = s.name;
22554         }
22555         if(!this.store){
22556             this.mode = 'local';
22557             var d = [], opts = s.options;
22558             for(var i = 0, len = opts.length;i < len; i++){
22559                 var o = opts[i];
22560                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22561                 if(o.selected) {
22562                     this.value = value;
22563                 }
22564                 d.push([value, o.text]);
22565             }
22566             this.store = new Roo.data.SimpleStore({
22567                 'id': 0,
22568                 fields: ['value', 'text'],
22569                 data : d
22570             });
22571             this.valueField = 'value';
22572             this.displayField = 'text';
22573         }
22574         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22575         if(!this.lazyRender){
22576             this.target = true;
22577             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22578             s.parentNode.removeChild(s); // remove it
22579             this.render(this.el.parentNode);
22580         }else{
22581             s.parentNode.removeChild(s); // remove it
22582         }
22583
22584     }
22585     if (this.store) {
22586         this.store = Roo.factory(this.store, Roo.data);
22587     }
22588     
22589     this.selectedIndex = -1;
22590     if(this.mode == 'local'){
22591         if(config.queryDelay === undefined){
22592             this.queryDelay = 10;
22593         }
22594         if(config.minChars === undefined){
22595             this.minChars = 0;
22596         }
22597     }
22598 };
22599
22600 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22601     /**
22602      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22603      */
22604     /**
22605      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22606      * rendering into an Roo.Editor, defaults to false)
22607      */
22608     /**
22609      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22610      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22611      */
22612     /**
22613      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22614      */
22615     /**
22616      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22617      * the dropdown list (defaults to undefined, with no header element)
22618      */
22619
22620      /**
22621      * @cfg {String/Roo.Template} tpl The template to use to render the output
22622      */
22623      
22624     // private
22625     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22626     /**
22627      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22628      */
22629     listWidth: undefined,
22630     /**
22631      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22632      * mode = 'remote' or 'text' if mode = 'local')
22633      */
22634     displayField: undefined,
22635     /**
22636      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22637      * mode = 'remote' or 'value' if mode = 'local'). 
22638      * Note: use of a valueField requires the user make a selection
22639      * in order for a value to be mapped.
22640      */
22641     valueField: undefined,
22642     /**
22643      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22644      * field's data value (defaults to the underlying DOM element's name)
22645      */
22646     hiddenName: undefined,
22647     /**
22648      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22649      */
22650     listClass: '',
22651     /**
22652      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22653      */
22654     selectedClass: 'x-combo-selected',
22655     /**
22656      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22657      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22658      * which displays a downward arrow icon).
22659      */
22660     triggerClass : 'x-form-arrow-trigger',
22661     /**
22662      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22663      */
22664     shadow:'sides',
22665     /**
22666      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22667      * anchor positions (defaults to 'tl-bl')
22668      */
22669     listAlign: 'tl-bl?',
22670     /**
22671      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22672      */
22673     maxHeight: 300,
22674     /**
22675      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22676      * query specified by the allQuery config option (defaults to 'query')
22677      */
22678     triggerAction: 'query',
22679     /**
22680      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22681      * (defaults to 4, does not apply if editable = false)
22682      */
22683     minChars : 4,
22684     /**
22685      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22686      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22687      */
22688     typeAhead: false,
22689     /**
22690      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22691      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22692      */
22693     queryDelay: 500,
22694     /**
22695      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22696      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22697      */
22698     pageSize: 0,
22699     /**
22700      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22701      * when editable = true (defaults to false)
22702      */
22703     selectOnFocus:false,
22704     /**
22705      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22706      */
22707     queryParam: 'query',
22708     /**
22709      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22710      * when mode = 'remote' (defaults to 'Loading...')
22711      */
22712     loadingText: 'Loading...',
22713     /**
22714      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22715      */
22716     resizable: false,
22717     /**
22718      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22719      */
22720     handleHeight : 8,
22721     /**
22722      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22723      * traditional select (defaults to true)
22724      */
22725     editable: true,
22726     /**
22727      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22728      */
22729     allQuery: '',
22730     /**
22731      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22732      */
22733     mode: 'remote',
22734     /**
22735      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22736      * listWidth has a higher value)
22737      */
22738     minListWidth : 70,
22739     /**
22740      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22741      * allow the user to set arbitrary text into the field (defaults to false)
22742      */
22743     forceSelection:false,
22744     /**
22745      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22746      * if typeAhead = true (defaults to 250)
22747      */
22748     typeAheadDelay : 250,
22749     /**
22750      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22751      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22752      */
22753     valueNotFoundText : undefined,
22754     /**
22755      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22756      */
22757     blockFocus : false,
22758     
22759     /**
22760      * @cfg {Boolean} disableClear Disable showing of clear button.
22761      */
22762     disableClear : false,
22763     /**
22764      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22765      */
22766     alwaysQuery : false,
22767     
22768     //private
22769     addicon : false,
22770     editicon: false,
22771     
22772     
22773     // private
22774     onRender : function(ct, position){
22775         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22776         if(this.hiddenName){
22777             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22778                     'before', true);
22779             this.hiddenField.value =
22780                 this.hiddenValue !== undefined ? this.hiddenValue :
22781                 this.value !== undefined ? this.value : '';
22782
22783             // prevent input submission
22784             this.el.dom.removeAttribute('name');
22785         }
22786         if(Roo.isGecko){
22787             this.el.dom.setAttribute('autocomplete', 'off');
22788         }
22789
22790         var cls = 'x-combo-list';
22791
22792         this.list = new Roo.Layer({
22793             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22794         });
22795
22796         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22797         this.list.setWidth(lw);
22798         this.list.swallowEvent('mousewheel');
22799         this.assetHeight = 0;
22800
22801         if(this.title){
22802             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22803             this.assetHeight += this.header.getHeight();
22804         }
22805
22806         this.innerList = this.list.createChild({cls:cls+'-inner'});
22807         this.innerList.on('mouseover', this.onViewOver, this);
22808         this.innerList.on('mousemove', this.onViewMove, this);
22809         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22810         
22811         if(this.allowBlank && !this.pageSize && !this.disableClear){
22812             this.footer = this.list.createChild({cls:cls+'-ft'});
22813             this.pageTb = new Roo.Toolbar(this.footer);
22814            
22815         }
22816         if(this.pageSize){
22817             this.footer = this.list.createChild({cls:cls+'-ft'});
22818             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22819                     {pageSize: this.pageSize});
22820             
22821         }
22822         
22823         if (this.pageTb && this.allowBlank && !this.disableClear) {
22824             var _this = this;
22825             this.pageTb.add(new Roo.Toolbar.Fill(), {
22826                 cls: 'x-btn-icon x-btn-clear',
22827                 text: '&#160;',
22828                 handler: function()
22829                 {
22830                     _this.collapse();
22831                     _this.clearValue();
22832                     _this.onSelect(false, -1);
22833                 }
22834             });
22835         }
22836         if (this.footer) {
22837             this.assetHeight += this.footer.getHeight();
22838         }
22839         
22840
22841         if(!this.tpl){
22842             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22843         }
22844
22845         this.view = new Roo.View(this.innerList, this.tpl, {
22846             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22847         });
22848
22849         this.view.on('click', this.onViewClick, this);
22850
22851         this.store.on('beforeload', this.onBeforeLoad, this);
22852         this.store.on('load', this.onLoad, this);
22853         this.store.on('loadexception', this.collapse, this);
22854
22855         if(this.resizable){
22856             this.resizer = new Roo.Resizable(this.list,  {
22857                pinned:true, handles:'se'
22858             });
22859             this.resizer.on('resize', function(r, w, h){
22860                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22861                 this.listWidth = w;
22862                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22863                 this.restrictHeight();
22864             }, this);
22865             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22866         }
22867         if(!this.editable){
22868             this.editable = true;
22869             this.setEditable(false);
22870         }  
22871         
22872         
22873         if (typeof(this.events.add.listeners) != 'undefined') {
22874             
22875             this.addicon = this.wrap.createChild(
22876                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22877        
22878             this.addicon.on('click', function(e) {
22879                 this.fireEvent('add', this);
22880             }, this);
22881         }
22882         if (typeof(this.events.edit.listeners) != 'undefined') {
22883             
22884             this.editicon = this.wrap.createChild(
22885                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22886             if (this.addicon) {
22887                 this.editicon.setStyle('margin-left', '40px');
22888             }
22889             this.editicon.on('click', function(e) {
22890                 
22891                 // we fire even  if inothing is selected..
22892                 this.fireEvent('edit', this, this.lastData );
22893                 
22894             }, this);
22895         }
22896         
22897         
22898         
22899     },
22900
22901     // private
22902     initEvents : function(){
22903         Roo.form.ComboBox.superclass.initEvents.call(this);
22904
22905         this.keyNav = new Roo.KeyNav(this.el, {
22906             "up" : function(e){
22907                 this.inKeyMode = true;
22908                 this.selectPrev();
22909             },
22910
22911             "down" : function(e){
22912                 if(!this.isExpanded()){
22913                     this.onTriggerClick();
22914                 }else{
22915                     this.inKeyMode = true;
22916                     this.selectNext();
22917                 }
22918             },
22919
22920             "enter" : function(e){
22921                 this.onViewClick();
22922                 //return true;
22923             },
22924
22925             "esc" : function(e){
22926                 this.collapse();
22927             },
22928
22929             "tab" : function(e){
22930                 this.onViewClick(false);
22931                 return true;
22932             },
22933
22934             scope : this,
22935
22936             doRelay : function(foo, bar, hname){
22937                 if(hname == 'down' || this.scope.isExpanded()){
22938                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22939                 }
22940                 return true;
22941             },
22942
22943             forceKeyDown: true
22944         });
22945         this.queryDelay = Math.max(this.queryDelay || 10,
22946                 this.mode == 'local' ? 10 : 250);
22947         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
22948         if(this.typeAhead){
22949             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
22950         }
22951         if(this.editable !== false){
22952             this.el.on("keyup", this.onKeyUp, this);
22953         }
22954         if(this.forceSelection){
22955             this.on('blur', this.doForce, this);
22956         }
22957     },
22958
22959     onDestroy : function(){
22960         if(this.view){
22961             this.view.setStore(null);
22962             this.view.el.removeAllListeners();
22963             this.view.el.remove();
22964             this.view.purgeListeners();
22965         }
22966         if(this.list){
22967             this.list.destroy();
22968         }
22969         if(this.store){
22970             this.store.un('beforeload', this.onBeforeLoad, this);
22971             this.store.un('load', this.onLoad, this);
22972             this.store.un('loadexception', this.collapse, this);
22973         }
22974         Roo.form.ComboBox.superclass.onDestroy.call(this);
22975     },
22976
22977     // private
22978     fireKey : function(e){
22979         if(e.isNavKeyPress() && !this.list.isVisible()){
22980             this.fireEvent("specialkey", this, e);
22981         }
22982     },
22983
22984     // private
22985     onResize: function(w, h){
22986         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
22987         
22988         if(typeof w != 'number'){
22989             // we do not handle it!?!?
22990             return;
22991         }
22992         var tw = this.trigger.getWidth();
22993         tw += this.addicon ? this.addicon.getWidth() : 0;
22994         tw += this.editicon ? this.editicon.getWidth() : 0;
22995         var x = w - tw;
22996         this.el.setWidth( this.adjustWidth('input', x));
22997             
22998         this.trigger.setStyle('left', x+'px');
22999         
23000         if(this.list && this.listWidth === undefined){
23001             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23002             this.list.setWidth(lw);
23003             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23004         }
23005         
23006     
23007         
23008     },
23009
23010     /**
23011      * Allow or prevent the user from directly editing the field text.  If false is passed,
23012      * the user will only be able to select from the items defined in the dropdown list.  This method
23013      * is the runtime equivalent of setting the 'editable' config option at config time.
23014      * @param {Boolean} value True to allow the user to directly edit the field text
23015      */
23016     setEditable : function(value){
23017         if(value == this.editable){
23018             return;
23019         }
23020         this.editable = value;
23021         if(!value){
23022             this.el.dom.setAttribute('readOnly', true);
23023             this.el.on('mousedown', this.onTriggerClick,  this);
23024             this.el.addClass('x-combo-noedit');
23025         }else{
23026             this.el.dom.setAttribute('readOnly', false);
23027             this.el.un('mousedown', this.onTriggerClick,  this);
23028             this.el.removeClass('x-combo-noedit');
23029         }
23030     },
23031
23032     // private
23033     onBeforeLoad : function(){
23034         if(!this.hasFocus){
23035             return;
23036         }
23037         this.innerList.update(this.loadingText ?
23038                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23039         this.restrictHeight();
23040         this.selectedIndex = -1;
23041     },
23042
23043     // private
23044     onLoad : function(){
23045         if(!this.hasFocus){
23046             return;
23047         }
23048         if(this.store.getCount() > 0){
23049             this.expand();
23050             this.restrictHeight();
23051             if(this.lastQuery == this.allQuery){
23052                 if(this.editable){
23053                     this.el.dom.select();
23054                 }
23055                 if(!this.selectByValue(this.value, true)){
23056                     this.select(0, true);
23057                 }
23058             }else{
23059                 this.selectNext();
23060                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23061                     this.taTask.delay(this.typeAheadDelay);
23062                 }
23063             }
23064         }else{
23065             this.onEmptyResults();
23066         }
23067         //this.el.focus();
23068     },
23069
23070     // private
23071     onTypeAhead : function(){
23072         if(this.store.getCount() > 0){
23073             var r = this.store.getAt(0);
23074             var newValue = r.data[this.displayField];
23075             var len = newValue.length;
23076             var selStart = this.getRawValue().length;
23077             if(selStart != len){
23078                 this.setRawValue(newValue);
23079                 this.selectText(selStart, newValue.length);
23080             }
23081         }
23082     },
23083
23084     // private
23085     onSelect : function(record, index){
23086         if(this.fireEvent('beforeselect', this, record, index) !== false){
23087             this.setFromData(index > -1 ? record.data : false);
23088             this.collapse();
23089             this.fireEvent('select', this, record, index);
23090         }
23091     },
23092
23093     /**
23094      * Returns the currently selected field value or empty string if no value is set.
23095      * @return {String} value The selected value
23096      */
23097     getValue : function(){
23098         if(this.valueField){
23099             return typeof this.value != 'undefined' ? this.value : '';
23100         }else{
23101             return Roo.form.ComboBox.superclass.getValue.call(this);
23102         }
23103     },
23104
23105     /**
23106      * Clears any text/value currently set in the field
23107      */
23108     clearValue : function(){
23109         if(this.hiddenField){
23110             this.hiddenField.value = '';
23111         }
23112         this.value = '';
23113         this.setRawValue('');
23114         this.lastSelectionText = '';
23115         this.applyEmptyText();
23116     },
23117
23118     /**
23119      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23120      * will be displayed in the field.  If the value does not match the data value of an existing item,
23121      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23122      * Otherwise the field will be blank (although the value will still be set).
23123      * @param {String} value The value to match
23124      */
23125     setValue : function(v){
23126         var text = v;
23127         if(this.valueField){
23128             var r = this.findRecord(this.valueField, v);
23129             if(r){
23130                 text = r.data[this.displayField];
23131             }else if(this.valueNotFoundText !== undefined){
23132                 text = this.valueNotFoundText;
23133             }
23134         }
23135         this.lastSelectionText = text;
23136         if(this.hiddenField){
23137             this.hiddenField.value = v;
23138         }
23139         Roo.form.ComboBox.superclass.setValue.call(this, text);
23140         this.value = v;
23141     },
23142     /**
23143      * @property {Object} the last set data for the element
23144      */
23145     
23146     lastData : false,
23147     /**
23148      * Sets the value of the field based on a object which is related to the record format for the store.
23149      * @param {Object} value the value to set as. or false on reset?
23150      */
23151     setFromData : function(o){
23152         var dv = ''; // display value
23153         var vv = ''; // value value..
23154         this.lastData = o;
23155         if (this.displayField) {
23156             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23157         } else {
23158             // this is an error condition!!!
23159             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23160         }
23161         
23162         if(this.valueField){
23163             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23164         }
23165         if(this.hiddenField){
23166             this.hiddenField.value = vv;
23167             
23168             this.lastSelectionText = dv;
23169             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23170             this.value = vv;
23171             return;
23172         }
23173         // no hidden field.. - we store the value in 'value', but still display
23174         // display field!!!!
23175         this.lastSelectionText = dv;
23176         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23177         this.value = vv;
23178         
23179         
23180     },
23181     // private
23182     reset : function(){
23183         // overridden so that last data is reset..
23184         this.setValue(this.originalValue);
23185         this.clearInvalid();
23186         this.lastData = false;
23187     },
23188     // private
23189     findRecord : function(prop, value){
23190         var record;
23191         if(this.store.getCount() > 0){
23192             this.store.each(function(r){
23193                 if(r.data[prop] == value){
23194                     record = r;
23195                     return false;
23196                 }
23197             });
23198         }
23199         return record;
23200     },
23201
23202     // private
23203     onViewMove : function(e, t){
23204         this.inKeyMode = false;
23205     },
23206
23207     // private
23208     onViewOver : function(e, t){
23209         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23210             return;
23211         }
23212         var item = this.view.findItemFromChild(t);
23213         if(item){
23214             var index = this.view.indexOf(item);
23215             this.select(index, false);
23216         }
23217     },
23218
23219     // private
23220     onViewClick : function(doFocus){
23221         var index = this.view.getSelectedIndexes()[0];
23222         var r = this.store.getAt(index);
23223         if(r){
23224             this.onSelect(r, index);
23225         }
23226         if(doFocus !== false && !this.blockFocus){
23227             this.el.focus();
23228         }
23229     },
23230
23231     // private
23232     restrictHeight : function(){
23233         this.innerList.dom.style.height = '';
23234         var inner = this.innerList.dom;
23235         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23236         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23237         this.list.beginUpdate();
23238         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23239         this.list.alignTo(this.el, this.listAlign);
23240         this.list.endUpdate();
23241     },
23242
23243     // private
23244     onEmptyResults : function(){
23245         this.collapse();
23246     },
23247
23248     /**
23249      * Returns true if the dropdown list is expanded, else false.
23250      */
23251     isExpanded : function(){
23252         return this.list.isVisible();
23253     },
23254
23255     /**
23256      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23257      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23258      * @param {String} value The data value of the item to select
23259      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23260      * selected item if it is not currently in view (defaults to true)
23261      * @return {Boolean} True if the value matched an item in the list, else false
23262      */
23263     selectByValue : function(v, scrollIntoView){
23264         if(v !== undefined && v !== null){
23265             var r = this.findRecord(this.valueField || this.displayField, v);
23266             if(r){
23267                 this.select(this.store.indexOf(r), scrollIntoView);
23268                 return true;
23269             }
23270         }
23271         return false;
23272     },
23273
23274     /**
23275      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23276      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23277      * @param {Number} index The zero-based index of the list item to select
23278      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23279      * selected item if it is not currently in view (defaults to true)
23280      */
23281     select : function(index, scrollIntoView){
23282         this.selectedIndex = index;
23283         this.view.select(index);
23284         if(scrollIntoView !== false){
23285             var el = this.view.getNode(index);
23286             if(el){
23287                 this.innerList.scrollChildIntoView(el, false);
23288             }
23289         }
23290     },
23291
23292     // private
23293     selectNext : function(){
23294         var ct = this.store.getCount();
23295         if(ct > 0){
23296             if(this.selectedIndex == -1){
23297                 this.select(0);
23298             }else if(this.selectedIndex < ct-1){
23299                 this.select(this.selectedIndex+1);
23300             }
23301         }
23302     },
23303
23304     // private
23305     selectPrev : function(){
23306         var ct = this.store.getCount();
23307         if(ct > 0){
23308             if(this.selectedIndex == -1){
23309                 this.select(0);
23310             }else if(this.selectedIndex != 0){
23311                 this.select(this.selectedIndex-1);
23312             }
23313         }
23314     },
23315
23316     // private
23317     onKeyUp : function(e){
23318         if(this.editable !== false && !e.isSpecialKey()){
23319             this.lastKey = e.getKey();
23320             this.dqTask.delay(this.queryDelay);
23321         }
23322     },
23323
23324     // private
23325     validateBlur : function(){
23326         return !this.list || !this.list.isVisible();   
23327     },
23328
23329     // private
23330     initQuery : function(){
23331         this.doQuery(this.getRawValue());
23332     },
23333
23334     // private
23335     doForce : function(){
23336         if(this.el.dom.value.length > 0){
23337             this.el.dom.value =
23338                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23339             this.applyEmptyText();
23340         }
23341     },
23342
23343     /**
23344      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23345      * query allowing the query action to be canceled if needed.
23346      * @param {String} query The SQL query to execute
23347      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23348      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23349      * saved in the current store (defaults to false)
23350      */
23351     doQuery : function(q, forceAll){
23352         if(q === undefined || q === null){
23353             q = '';
23354         }
23355         var qe = {
23356             query: q,
23357             forceAll: forceAll,
23358             combo: this,
23359             cancel:false
23360         };
23361         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23362             return false;
23363         }
23364         q = qe.query;
23365         forceAll = qe.forceAll;
23366         if(forceAll === true || (q.length >= this.minChars)){
23367             if(this.lastQuery != q || this.alwaysQuery){
23368                 this.lastQuery = q;
23369                 if(this.mode == 'local'){
23370                     this.selectedIndex = -1;
23371                     if(forceAll){
23372                         this.store.clearFilter();
23373                     }else{
23374                         this.store.filter(this.displayField, q);
23375                     }
23376                     this.onLoad();
23377                 }else{
23378                     this.store.baseParams[this.queryParam] = q;
23379                     this.store.load({
23380                         params: this.getParams(q)
23381                     });
23382                     this.expand();
23383                 }
23384             }else{
23385                 this.selectedIndex = -1;
23386                 this.onLoad();   
23387             }
23388         }
23389     },
23390
23391     // private
23392     getParams : function(q){
23393         var p = {};
23394         //p[this.queryParam] = q;
23395         if(this.pageSize){
23396             p.start = 0;
23397             p.limit = this.pageSize;
23398         }
23399         return p;
23400     },
23401
23402     /**
23403      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23404      */
23405     collapse : function(){
23406         if(!this.isExpanded()){
23407             return;
23408         }
23409         this.list.hide();
23410         Roo.get(document).un('mousedown', this.collapseIf, this);
23411         Roo.get(document).un('mousewheel', this.collapseIf, this);
23412         if (!this.editable) {
23413             Roo.get(document).un('keydown', this.listKeyPress, this);
23414         }
23415         this.fireEvent('collapse', this);
23416     },
23417
23418     // private
23419     collapseIf : function(e){
23420         if(!e.within(this.wrap) && !e.within(this.list)){
23421             this.collapse();
23422         }
23423     },
23424
23425     /**
23426      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23427      */
23428     expand : function(){
23429         if(this.isExpanded() || !this.hasFocus){
23430             return;
23431         }
23432         this.list.alignTo(this.el, this.listAlign);
23433         this.list.show();
23434         Roo.get(document).on('mousedown', this.collapseIf, this);
23435         Roo.get(document).on('mousewheel', this.collapseIf, this);
23436         if (!this.editable) {
23437             Roo.get(document).on('keydown', this.listKeyPress, this);
23438         }
23439         
23440         this.fireEvent('expand', this);
23441     },
23442
23443     // private
23444     // Implements the default empty TriggerField.onTriggerClick function
23445     onTriggerClick : function(){
23446         if(this.disabled){
23447             return;
23448         }
23449         if(this.isExpanded()){
23450             this.collapse();
23451             if (!this.blockFocus) {
23452                 this.el.focus();
23453             }
23454             
23455         }else {
23456             this.hasFocus = true;
23457             if(this.triggerAction == 'all') {
23458                 this.doQuery(this.allQuery, true);
23459             } else {
23460                 this.doQuery(this.getRawValue());
23461             }
23462             if (!this.blockFocus) {
23463                 this.el.focus();
23464             }
23465         }
23466     },
23467     listKeyPress : function(e)
23468     {
23469         //Roo.log('listkeypress');
23470         // scroll to first matching element based on key pres..
23471         if (e.isSpecialKey()) {
23472             return false;
23473         }
23474         var k = String.fromCharCode(e.getKey()).toUpperCase();
23475         //Roo.log(k);
23476         var match  = false;
23477         var csel = this.view.getSelectedNodes();
23478         var cselitem = false;
23479         if (csel.length) {
23480             var ix = this.view.indexOf(csel[0]);
23481             cselitem  = this.store.getAt(ix);
23482             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23483                 cselitem = false;
23484             }
23485             
23486         }
23487         
23488         this.store.each(function(v) { 
23489             if (cselitem) {
23490                 // start at existing selection.
23491                 if (cselitem.id == v.id) {
23492                     cselitem = false;
23493                 }
23494                 return;
23495             }
23496                 
23497             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23498                 match = this.store.indexOf(v);
23499                 return false;
23500             }
23501         }, this);
23502         
23503         if (match === false) {
23504             return true; // no more action?
23505         }
23506         // scroll to?
23507         this.view.select(match);
23508         var sn = Roo.get(this.view.getSelectedNodes()[0])
23509         sn.scrollIntoView(sn.dom.parentNode, false);
23510     }
23511
23512     /** 
23513     * @cfg {Boolean} grow 
23514     * @hide 
23515     */
23516     /** 
23517     * @cfg {Number} growMin 
23518     * @hide 
23519     */
23520     /** 
23521     * @cfg {Number} growMax 
23522     * @hide 
23523     */
23524     /**
23525      * @hide
23526      * @method autoSize
23527      */
23528 });/*
23529  * Based on:
23530  * Ext JS Library 1.1.1
23531  * Copyright(c) 2006-2007, Ext JS, LLC.
23532  *
23533  * Originally Released Under LGPL - original licence link has changed is not relivant.
23534  *
23535  * Fork - LGPL
23536  * <script type="text/javascript">
23537  */
23538 /**
23539  * @class Roo.form.Checkbox
23540  * @extends Roo.form.Field
23541  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23542  * @constructor
23543  * Creates a new Checkbox
23544  * @param {Object} config Configuration options
23545  */
23546 Roo.form.Checkbox = function(config){
23547     Roo.form.Checkbox.superclass.constructor.call(this, config);
23548     this.addEvents({
23549         /**
23550          * @event check
23551          * Fires when the checkbox is checked or unchecked.
23552              * @param {Roo.form.Checkbox} this This checkbox
23553              * @param {Boolean} checked The new checked value
23554              */
23555         check : true
23556     });
23557 };
23558
23559 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23560     /**
23561      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23562      */
23563     focusClass : undefined,
23564     /**
23565      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23566      */
23567     fieldClass: "x-form-field",
23568     /**
23569      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23570      */
23571     checked: false,
23572     /**
23573      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23574      * {tag: "input", type: "checkbox", autocomplete: "off"})
23575      */
23576     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23577     /**
23578      * @cfg {String} boxLabel The text that appears beside the checkbox
23579      */
23580     boxLabel : "",
23581     /**
23582      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23583      */  
23584     inputValue : '1',
23585     /**
23586      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23587      */
23588      valueOff: '0', // value when not checked..
23589
23590     actionMode : 'viewEl', 
23591     //
23592     // private
23593     itemCls : 'x-menu-check-item x-form-item',
23594     groupClass : 'x-menu-group-item',
23595     inputType : 'hidden',
23596     
23597     
23598     inSetChecked: false, // check that we are not calling self...
23599     
23600     inputElement: false, // real input element?
23601     basedOn: false, // ????
23602     
23603     isFormField: true, // not sure where this is needed!!!!
23604
23605     onResize : function(){
23606         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23607         if(!this.boxLabel){
23608             this.el.alignTo(this.wrap, 'c-c');
23609         }
23610     },
23611
23612     initEvents : function(){
23613         Roo.form.Checkbox.superclass.initEvents.call(this);
23614         this.el.on("click", this.onClick,  this);
23615         this.el.on("change", this.onClick,  this);
23616     },
23617
23618
23619     getResizeEl : function(){
23620         return this.wrap;
23621     },
23622
23623     getPositionEl : function(){
23624         return this.wrap;
23625     },
23626
23627     // private
23628     onRender : function(ct, position){
23629         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23630         /*
23631         if(this.inputValue !== undefined){
23632             this.el.dom.value = this.inputValue;
23633         }
23634         */
23635         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23636         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23637         var viewEl = this.wrap.createChild({ 
23638             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23639         this.viewEl = viewEl;   
23640         this.wrap.on('click', this.onClick,  this); 
23641         
23642         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23643         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23644         
23645         
23646         
23647         if(this.boxLabel){
23648             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23649         //    viewEl.on('click', this.onClick,  this); 
23650         }
23651         //if(this.checked){
23652             this.setChecked(this.checked);
23653         //}else{
23654             //this.checked = this.el.dom;
23655         //}
23656
23657     },
23658
23659     // private
23660     initValue : Roo.emptyFn,
23661
23662     /**
23663      * Returns the checked state of the checkbox.
23664      * @return {Boolean} True if checked, else false
23665      */
23666     getValue : function(){
23667         if(this.el){
23668             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23669         }
23670         return this.valueOff;
23671         
23672     },
23673
23674         // private
23675     onClick : function(){ 
23676         this.setChecked(!this.checked);
23677
23678         //if(this.el.dom.checked != this.checked){
23679         //    this.setValue(this.el.dom.checked);
23680        // }
23681     },
23682
23683     /**
23684      * Sets the checked state of the checkbox.
23685      * On is always based on a string comparison between inputValue and the param.
23686      * @param {Boolean/String} value - the value to set 
23687      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23688      */
23689     setValue : function(v,suppressEvent){
23690         
23691         
23692         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23693         //if(this.el && this.el.dom){
23694         //    this.el.dom.checked = this.checked;
23695         //    this.el.dom.defaultChecked = this.checked;
23696         //}
23697         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23698         //this.fireEvent("check", this, this.checked);
23699     },
23700     // private..
23701     setChecked : function(state,suppressEvent)
23702     {
23703         if (this.inSetChecked) {
23704             this.checked = state;
23705             return;
23706         }
23707         
23708     
23709         if(this.wrap){
23710             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23711         }
23712         this.checked = state;
23713         if(suppressEvent !== true){
23714             this.fireEvent('check', this, state);
23715         }
23716         this.inSetChecked = true;
23717         this.el.dom.value = state ? this.inputValue : this.valueOff;
23718         this.inSetChecked = false;
23719         
23720     },
23721     // handle setting of hidden value by some other method!!?!?
23722     setFromHidden: function()
23723     {
23724         if(!this.el){
23725             return;
23726         }
23727         //console.log("SET FROM HIDDEN");
23728         //alert('setFrom hidden');
23729         this.setValue(this.el.dom.value);
23730     },
23731     
23732     onDestroy : function()
23733     {
23734         if(this.viewEl){
23735             Roo.get(this.viewEl).remove();
23736         }
23737          
23738         Roo.form.Checkbox.superclass.onDestroy.call(this);
23739     }
23740
23741 });/*
23742  * Based on:
23743  * Ext JS Library 1.1.1
23744  * Copyright(c) 2006-2007, Ext JS, LLC.
23745  *
23746  * Originally Released Under LGPL - original licence link has changed is not relivant.
23747  *
23748  * Fork - LGPL
23749  * <script type="text/javascript">
23750  */
23751  
23752 /**
23753  * @class Roo.form.Radio
23754  * @extends Roo.form.Checkbox
23755  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23756  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23757  * @constructor
23758  * Creates a new Radio
23759  * @param {Object} config Configuration options
23760  */
23761 Roo.form.Radio = function(){
23762     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23763 };
23764 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23765     inputType: 'radio',
23766
23767     /**
23768      * If this radio is part of a group, it will return the selected value
23769      * @return {String}
23770      */
23771     getGroupValue : function(){
23772         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23773     }
23774 });//<script type="text/javascript">
23775
23776 /*
23777  * Ext JS Library 1.1.1
23778  * Copyright(c) 2006-2007, Ext JS, LLC.
23779  * licensing@extjs.com
23780  * 
23781  * http://www.extjs.com/license
23782  */
23783  
23784  /*
23785   * 
23786   * Known bugs:
23787   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23788   * - IE ? - no idea how much works there.
23789   * 
23790   * 
23791   * 
23792   */
23793  
23794
23795 /**
23796  * @class Ext.form.HtmlEditor
23797  * @extends Ext.form.Field
23798  * Provides a lightweight HTML Editor component.
23799  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23800  * 
23801  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23802  * supported by this editor.</b><br/><br/>
23803  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23804  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23805  */
23806 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23807       /**
23808      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23809      */
23810     toolbars : false,
23811     /**
23812      * @cfg {String} createLinkText The default text for the create link prompt
23813      */
23814     createLinkText : 'Please enter the URL for the link:',
23815     /**
23816      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23817      */
23818     defaultLinkValue : 'http:/'+'/',
23819    
23820     
23821     // id of frame..
23822     frameId: false,
23823     
23824     // private properties
23825     validationEvent : false,
23826     deferHeight: true,
23827     initialized : false,
23828     activated : false,
23829     sourceEditMode : false,
23830     onFocus : Roo.emptyFn,
23831     iframePad:3,
23832     hideMode:'offsets',
23833     defaultAutoCreate : {
23834         tag: "textarea",
23835         style:"width:500px;height:300px;",
23836         autocomplete: "off"
23837     },
23838
23839     // private
23840     initComponent : function(){
23841         this.addEvents({
23842             /**
23843              * @event initialize
23844              * Fires when the editor is fully initialized (including the iframe)
23845              * @param {HtmlEditor} this
23846              */
23847             initialize: true,
23848             /**
23849              * @event activate
23850              * Fires when the editor is first receives the focus. Any insertion must wait
23851              * until after this event.
23852              * @param {HtmlEditor} this
23853              */
23854             activate: true,
23855              /**
23856              * @event beforesync
23857              * Fires before the textarea is updated with content from the editor iframe. Return false
23858              * to cancel the sync.
23859              * @param {HtmlEditor} this
23860              * @param {String} html
23861              */
23862             beforesync: true,
23863              /**
23864              * @event beforepush
23865              * Fires before the iframe editor is updated with content from the textarea. Return false
23866              * to cancel the push.
23867              * @param {HtmlEditor} this
23868              * @param {String} html
23869              */
23870             beforepush: true,
23871              /**
23872              * @event sync
23873              * Fires when the textarea is updated with content from the editor iframe.
23874              * @param {HtmlEditor} this
23875              * @param {String} html
23876              */
23877             sync: true,
23878              /**
23879              * @event push
23880              * Fires when the iframe editor is updated with content from the textarea.
23881              * @param {HtmlEditor} this
23882              * @param {String} html
23883              */
23884             push: true,
23885              /**
23886              * @event editmodechange
23887              * Fires when the editor switches edit modes
23888              * @param {HtmlEditor} this
23889              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23890              */
23891             editmodechange: true,
23892             /**
23893              * @event editorevent
23894              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23895              * @param {HtmlEditor} this
23896              */
23897             editorevent: true
23898         })
23899     },
23900
23901     /**
23902      * Protected method that will not generally be called directly. It
23903      * is called when the editor creates its toolbar. Override this method if you need to
23904      * add custom toolbar buttons.
23905      * @param {HtmlEditor} editor
23906      */
23907     createToolbar : function(editor){
23908         if (!editor.toolbars || !editor.toolbars.length) {
23909             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23910         }
23911         
23912         for (var i =0 ; i < editor.toolbars.length;i++) {
23913             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23914             editor.toolbars[i].init(editor);
23915         }
23916          
23917         
23918     },
23919
23920     /**
23921      * Protected method that will not generally be called directly. It
23922      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23923      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23924      */
23925     getDocMarkup : function(){
23926         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23927     },
23928
23929     // private
23930     onRender : function(ct, position){
23931         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23932         this.el.dom.style.border = '0 none';
23933         this.el.dom.setAttribute('tabIndex', -1);
23934         this.el.addClass('x-hidden');
23935         if(Roo.isIE){ // fix IE 1px bogus margin
23936             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23937         }
23938         this.wrap = this.el.wrap({
23939             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23940         });
23941
23942         this.frameId = Roo.id();
23943         this.createToolbar(this);
23944         
23945         
23946         
23947         
23948       
23949         
23950         var iframe = this.wrap.createChild({
23951             tag: 'iframe',
23952             id: this.frameId,
23953             name: this.frameId,
23954             frameBorder : 'no',
23955             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23956         });
23957         
23958        // console.log(iframe);
23959         //this.wrap.dom.appendChild(iframe);
23960
23961         this.iframe = iframe.dom;
23962
23963          this.assignDocWin();
23964         
23965         this.doc.designMode = 'on';
23966        
23967         this.doc.open();
23968         this.doc.write(this.getDocMarkup());
23969         this.doc.close();
23970
23971         
23972         var task = { // must defer to wait for browser to be ready
23973             run : function(){
23974                 //console.log("run task?" + this.doc.readyState);
23975                 this.assignDocWin();
23976                 if(this.doc.body || this.doc.readyState == 'complete'){
23977                     try {
23978                         this.doc.designMode="on";
23979                     } catch (e) {
23980                         return;
23981                     }
23982                     Roo.TaskMgr.stop(task);
23983                     this.initEditor.defer(10, this);
23984                 }
23985             },
23986             interval : 10,
23987             duration:10000,
23988             scope: this
23989         };
23990         Roo.TaskMgr.start(task);
23991
23992         if(!this.width){
23993             this.setSize(this.el.getSize());
23994         }
23995     },
23996
23997     // private
23998     onResize : function(w, h){
23999         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24000         if(this.el && this.iframe){
24001             if(typeof w == 'number'){
24002                 var aw = w - this.wrap.getFrameWidth('lr');
24003                 this.el.setWidth(this.adjustWidth('textarea', aw));
24004                 this.iframe.style.width = aw + 'px';
24005             }
24006             if(typeof h == 'number'){
24007                 var tbh = 0;
24008                 for (var i =0; i < this.toolbars.length;i++) {
24009                     // fixme - ask toolbars for heights?
24010                     tbh += this.toolbars[i].tb.el.getHeight();
24011                 }
24012                 
24013                 
24014                 
24015                 
24016                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24017                 this.el.setHeight(this.adjustWidth('textarea', ah));
24018                 this.iframe.style.height = ah + 'px';
24019                 if(this.doc){
24020                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24021                 }
24022             }
24023         }
24024     },
24025
24026     /**
24027      * Toggles the editor between standard and source edit mode.
24028      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24029      */
24030     toggleSourceEdit : function(sourceEditMode){
24031         
24032         this.sourceEditMode = sourceEditMode === true;
24033         
24034         if(this.sourceEditMode){
24035           
24036             this.syncValue();
24037             this.iframe.className = 'x-hidden';
24038             this.el.removeClass('x-hidden');
24039             this.el.dom.removeAttribute('tabIndex');
24040             this.el.focus();
24041         }else{
24042              
24043             this.pushValue();
24044             this.iframe.className = '';
24045             this.el.addClass('x-hidden');
24046             this.el.dom.setAttribute('tabIndex', -1);
24047             this.deferFocus();
24048         }
24049         this.setSize(this.wrap.getSize());
24050         this.fireEvent('editmodechange', this, this.sourceEditMode);
24051     },
24052
24053     // private used internally
24054     createLink : function(){
24055         var url = prompt(this.createLinkText, this.defaultLinkValue);
24056         if(url && url != 'http:/'+'/'){
24057             this.relayCmd('createlink', url);
24058         }
24059     },
24060
24061     // private (for BoxComponent)
24062     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24063
24064     // private (for BoxComponent)
24065     getResizeEl : function(){
24066         return this.wrap;
24067     },
24068
24069     // private (for BoxComponent)
24070     getPositionEl : function(){
24071         return this.wrap;
24072     },
24073
24074     // private
24075     initEvents : function(){
24076         this.originalValue = this.getValue();
24077     },
24078
24079     /**
24080      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24081      * @method
24082      */
24083     markInvalid : Roo.emptyFn,
24084     /**
24085      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24086      * @method
24087      */
24088     clearInvalid : Roo.emptyFn,
24089
24090     setValue : function(v){
24091         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24092         this.pushValue();
24093     },
24094
24095     /**
24096      * Protected method that will not generally be called directly. If you need/want
24097      * custom HTML cleanup, this is the method you should override.
24098      * @param {String} html The HTML to be cleaned
24099      * return {String} The cleaned HTML
24100      */
24101     cleanHtml : function(html){
24102         html = String(html);
24103         if(html.length > 5){
24104             if(Roo.isSafari){ // strip safari nonsense
24105                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24106             }
24107         }
24108         if(html == '&nbsp;'){
24109             html = '';
24110         }
24111         return html;
24112     },
24113
24114     /**
24115      * Protected method that will not generally be called directly. Syncs the contents
24116      * of the editor iframe with the textarea.
24117      */
24118     syncValue : function(){
24119         if(this.initialized){
24120             var bd = (this.doc.body || this.doc.documentElement);
24121             var html = bd.innerHTML;
24122             if(Roo.isSafari){
24123                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24124                 var m = bs.match(/text-align:(.*?);/i);
24125                 if(m && m[1]){
24126                     html = '<div style="'+m[0]+'">' + html + '</div>';
24127                 }
24128             }
24129             html = this.cleanHtml(html);
24130             if(this.fireEvent('beforesync', this, html) !== false){
24131                 this.el.dom.value = html;
24132                 this.fireEvent('sync', this, html);
24133             }
24134         }
24135     },
24136
24137     /**
24138      * Protected method that will not generally be called directly. Pushes the value of the textarea
24139      * into the iframe editor.
24140      */
24141     pushValue : function(){
24142         if(this.initialized){
24143             var v = this.el.dom.value;
24144             if(v.length < 1){
24145                 v = '&#160;';
24146             }
24147             if(this.fireEvent('beforepush', this, v) !== false){
24148                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24149                 this.fireEvent('push', this, v);
24150             }
24151         }
24152     },
24153
24154     // private
24155     deferFocus : function(){
24156         this.focus.defer(10, this);
24157     },
24158
24159     // doc'ed in Field
24160     focus : function(){
24161         if(this.win && !this.sourceEditMode){
24162             this.win.focus();
24163         }else{
24164             this.el.focus();
24165         }
24166     },
24167     
24168     assignDocWin: function()
24169     {
24170         var iframe = this.iframe;
24171         
24172          if(Roo.isIE){
24173             this.doc = iframe.contentWindow.document;
24174             this.win = iframe.contentWindow;
24175         } else {
24176             if (!Roo.get(this.frameId)) {
24177                 return;
24178             }
24179             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24180             this.win = Roo.get(this.frameId).dom.contentWindow;
24181         }
24182     },
24183     
24184     // private
24185     initEditor : function(){
24186         //console.log("INIT EDITOR");
24187         this.assignDocWin();
24188         
24189         
24190         
24191         this.doc.designMode="on";
24192         this.doc.open();
24193         this.doc.write(this.getDocMarkup());
24194         this.doc.close();
24195         
24196         var dbody = (this.doc.body || this.doc.documentElement);
24197         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24198         // this copies styles from the containing element into thsi one..
24199         // not sure why we need all of this..
24200         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24201         ss['background-attachment'] = 'fixed'; // w3c
24202         dbody.bgProperties = 'fixed'; // ie
24203         Roo.DomHelper.applyStyles(dbody, ss);
24204         Roo.EventManager.on(this.doc, {
24205             'mousedown': this.onEditorEvent,
24206             'dblclick': this.onEditorEvent,
24207             'click': this.onEditorEvent,
24208             'keyup': this.onEditorEvent,
24209             buffer:100,
24210             scope: this
24211         });
24212         if(Roo.isGecko){
24213             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24214         }
24215         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24216             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24217         }
24218         this.initialized = true;
24219
24220         this.fireEvent('initialize', this);
24221         this.pushValue();
24222     },
24223
24224     // private
24225     onDestroy : function(){
24226         
24227         
24228         
24229         if(this.rendered){
24230             
24231             for (var i =0; i < this.toolbars.length;i++) {
24232                 // fixme - ask toolbars for heights?
24233                 this.toolbars[i].onDestroy();
24234             }
24235             
24236             this.wrap.dom.innerHTML = '';
24237             this.wrap.remove();
24238         }
24239     },
24240
24241     // private
24242     onFirstFocus : function(){
24243         
24244         this.assignDocWin();
24245         
24246         
24247         this.activated = true;
24248         for (var i =0; i < this.toolbars.length;i++) {
24249             this.toolbars[i].onFirstFocus();
24250         }
24251        
24252         if(Roo.isGecko){ // prevent silly gecko errors
24253             this.win.focus();
24254             var s = this.win.getSelection();
24255             if(!s.focusNode || s.focusNode.nodeType != 3){
24256                 var r = s.getRangeAt(0);
24257                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24258                 r.collapse(true);
24259                 this.deferFocus();
24260             }
24261             try{
24262                 this.execCmd('useCSS', true);
24263                 this.execCmd('styleWithCSS', false);
24264             }catch(e){}
24265         }
24266         this.fireEvent('activate', this);
24267     },
24268
24269     // private
24270     adjustFont: function(btn){
24271         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24272         //if(Roo.isSafari){ // safari
24273         //    adjust *= 2;
24274        // }
24275         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24276         if(Roo.isSafari){ // safari
24277             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24278             v =  (v < 10) ? 10 : v;
24279             v =  (v > 48) ? 48 : v;
24280             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24281             
24282         }
24283         
24284         
24285         v = Math.max(1, v+adjust);
24286         
24287         this.execCmd('FontSize', v  );
24288     },
24289
24290     onEditorEvent : function(e){
24291         this.fireEvent('editorevent', this, e);
24292       //  this.updateToolbar();
24293         this.syncValue();
24294     },
24295
24296     insertTag : function(tg)
24297     {
24298         // could be a bit smarter... -> wrap the current selected tRoo..
24299         
24300         this.execCmd("formatblock",   tg);
24301         
24302     },
24303     
24304     insertText : function(txt)
24305     {
24306         
24307         
24308         range = this.createRange();
24309         range.deleteContents();
24310                //alert(Sender.getAttribute('label'));
24311                
24312         range.insertNode(this.doc.createTextNode(txt));
24313     } ,
24314     
24315     // private
24316     relayBtnCmd : function(btn){
24317         this.relayCmd(btn.cmd);
24318     },
24319
24320     /**
24321      * Executes a Midas editor command on the editor document and performs necessary focus and
24322      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24323      * @param {String} cmd The Midas command
24324      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24325      */
24326     relayCmd : function(cmd, value){
24327         this.win.focus();
24328         this.execCmd(cmd, value);
24329         this.fireEvent('editorevent', this);
24330         //this.updateToolbar();
24331         this.deferFocus();
24332     },
24333
24334     /**
24335      * Executes a Midas editor command directly on the editor document.
24336      * For visual commands, you should use {@link #relayCmd} instead.
24337      * <b>This should only be called after the editor is initialized.</b>
24338      * @param {String} cmd The Midas command
24339      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24340      */
24341     execCmd : function(cmd, value){
24342         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24343         this.syncValue();
24344     },
24345
24346    
24347     /**
24348      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24349      * to insert tRoo.
24350      * @param {String} text
24351      */
24352     insertAtCursor : function(text){
24353         if(!this.activated){
24354             return;
24355         }
24356         if(Roo.isIE){
24357             this.win.focus();
24358             var r = this.doc.selection.createRange();
24359             if(r){
24360                 r.collapse(true);
24361                 r.pasteHTML(text);
24362                 this.syncValue();
24363                 this.deferFocus();
24364             }
24365         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24366             this.win.focus();
24367             this.execCmd('InsertHTML', text);
24368             this.deferFocus();
24369         }
24370     },
24371  // private
24372     mozKeyPress : function(e){
24373         if(e.ctrlKey){
24374             var c = e.getCharCode(), cmd;
24375           
24376             if(c > 0){
24377                 c = String.fromCharCode(c).toLowerCase();
24378                 switch(c){
24379                     case 'b':
24380                         cmd = 'bold';
24381                     break;
24382                     case 'i':
24383                         cmd = 'italic';
24384                     break;
24385                     case 'u':
24386                         cmd = 'underline';
24387                     case 'v':
24388                         this.cleanUpPaste.defer(100, this);
24389                         return;
24390                     break;
24391                 }
24392                 if(cmd){
24393                     this.win.focus();
24394                     this.execCmd(cmd);
24395                     this.deferFocus();
24396                     e.preventDefault();
24397                 }
24398                 
24399             }
24400         }
24401     },
24402
24403     // private
24404     fixKeys : function(){ // load time branching for fastest keydown performance
24405         if(Roo.isIE){
24406             return function(e){
24407                 var k = e.getKey(), r;
24408                 if(k == e.TAB){
24409                     e.stopEvent();
24410                     r = this.doc.selection.createRange();
24411                     if(r){
24412                         r.collapse(true);
24413                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24414                         this.deferFocus();
24415                     }
24416                     return;
24417                 }
24418                 
24419                 if(k == e.ENTER){
24420                     r = this.doc.selection.createRange();
24421                     if(r){
24422                         var target = r.parentElement();
24423                         if(!target || target.tagName.toLowerCase() != 'li'){
24424                             e.stopEvent();
24425                             r.pasteHTML('<br />');
24426                             r.collapse(false);
24427                             r.select();
24428                         }
24429                     }
24430                 }
24431                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24432                     this.cleanUpPaste.defer(100, this);
24433                     return;
24434                 }
24435                 
24436                 
24437             };
24438         }else if(Roo.isOpera){
24439             return function(e){
24440                 var k = e.getKey();
24441                 if(k == e.TAB){
24442                     e.stopEvent();
24443                     this.win.focus();
24444                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24445                     this.deferFocus();
24446                 }
24447                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24448                     this.cleanUpPaste.defer(100, this);
24449                     return;
24450                 }
24451                 
24452             };
24453         }else if(Roo.isSafari){
24454             return function(e){
24455                 var k = e.getKey();
24456                 
24457                 if(k == e.TAB){
24458                     e.stopEvent();
24459                     this.execCmd('InsertText','\t');
24460                     this.deferFocus();
24461                     return;
24462                 }
24463                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24464                     this.cleanUpPaste.defer(100, this);
24465                     return;
24466                 }
24467                 
24468              };
24469         }
24470     }(),
24471     
24472     getAllAncestors: function()
24473     {
24474         var p = this.getSelectedNode();
24475         var a = [];
24476         if (!p) {
24477             a.push(p); // push blank onto stack..
24478             p = this.getParentElement();
24479         }
24480         
24481         
24482         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24483             a.push(p);
24484             p = p.parentNode;
24485         }
24486         a.push(this.doc.body);
24487         return a;
24488     },
24489     lastSel : false,
24490     lastSelNode : false,
24491     
24492     
24493     getSelection : function() 
24494     {
24495         this.assignDocWin();
24496         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24497     },
24498     
24499     getSelectedNode: function() 
24500     {
24501         // this may only work on Gecko!!!
24502         
24503         // should we cache this!!!!
24504         
24505         
24506         
24507          
24508         var range = this.createRange(this.getSelection());
24509         
24510         if (Roo.isIE) {
24511             var parent = range.parentElement();
24512             while (true) {
24513                 var testRange = range.duplicate();
24514                 testRange.moveToElementText(parent);
24515                 if (testRange.inRange(range)) {
24516                     break;
24517                 }
24518                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24519                     break;
24520                 }
24521                 parent = parent.parentElement;
24522             }
24523             return parent;
24524         }
24525         
24526         
24527         var ar = range.endContainer.childNodes;
24528         if (!ar.length) {
24529             ar = range.commonAncestorContainer.childNodes;
24530             //alert(ar.length);
24531         }
24532         var nodes = [];
24533         var other_nodes = [];
24534         var has_other_nodes = false;
24535         for (var i=0;i<ar.length;i++) {
24536             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24537                 continue;
24538             }
24539             // fullly contained node.
24540             
24541             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24542                 nodes.push(ar[i]);
24543                 continue;
24544             }
24545             
24546             // probably selected..
24547             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24548                 other_nodes.push(ar[i]);
24549                 continue;
24550             }
24551             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24552                 continue;
24553             }
24554             
24555             
24556             has_other_nodes = true;
24557         }
24558         if (!nodes.length && other_nodes.length) {
24559             nodes= other_nodes;
24560         }
24561         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24562             return false;
24563         }
24564         
24565         return nodes[0];
24566     },
24567     createRange: function(sel)
24568     {
24569         // this has strange effects when using with 
24570         // top toolbar - not sure if it's a great idea.
24571         //this.editor.contentWindow.focus();
24572         if (typeof sel != "undefined") {
24573             try {
24574                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24575             } catch(e) {
24576                 return this.doc.createRange();
24577             }
24578         } else {
24579             return this.doc.createRange();
24580         }
24581     },
24582     getParentElement: function()
24583     {
24584         
24585         this.assignDocWin();
24586         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24587         
24588         var range = this.createRange(sel);
24589          
24590         try {
24591             var p = range.commonAncestorContainer;
24592             while (p.nodeType == 3) { // text node
24593                 p = p.parentNode;
24594             }
24595             return p;
24596         } catch (e) {
24597             return null;
24598         }
24599     
24600     },
24601     
24602     
24603     
24604     // BC Hacks - cause I cant work out what i was trying to do..
24605     rangeIntersectsNode : function(range, node)
24606     {
24607         var nodeRange = node.ownerDocument.createRange();
24608         try {
24609             nodeRange.selectNode(node);
24610         }
24611         catch (e) {
24612             nodeRange.selectNodeContents(node);
24613         }
24614
24615         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24616                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24617     },
24618     rangeCompareNode : function(range, node) {
24619         var nodeRange = node.ownerDocument.createRange();
24620         try {
24621             nodeRange.selectNode(node);
24622         } catch (e) {
24623             nodeRange.selectNodeContents(node);
24624         }
24625         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24626         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24627
24628         if (nodeIsBefore && !nodeIsAfter)
24629             return 0;
24630         if (!nodeIsBefore && nodeIsAfter)
24631             return 1;
24632         if (nodeIsBefore && nodeIsAfter)
24633             return 2;
24634
24635         return 3;
24636     },
24637
24638     // private? - in a new class?
24639     cleanUpPaste :  function()
24640     {
24641         // cleans up the whole document..
24642       //  console.log('cleanuppaste');
24643         this.cleanUpChildren(this.doc.body)
24644         
24645         
24646     },
24647     cleanUpChildren : function (n)
24648     {
24649         if (!n.childNodes.length) {
24650             return;
24651         }
24652         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24653            this.cleanUpChild(n.childNodes[i]);
24654         }
24655     },
24656     
24657     
24658         
24659     
24660     cleanUpChild : function (node)
24661     {
24662         //console.log(node);
24663         if (node.nodeName == "#text") {
24664             // clean up silly Windows -- stuff?
24665             return; 
24666         }
24667         if (node.nodeName == "#comment") {
24668             node.parentNode.removeChild(node);
24669             // clean up silly Windows -- stuff?
24670             return; 
24671         }
24672         
24673         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24674             // remove node.
24675             node.parentNode.removeChild(node);
24676             return;
24677             
24678         }
24679         if (!node.attributes || !node.attributes.length) {
24680             this.cleanUpChildren(node);
24681             return;
24682         }
24683         
24684         function cleanAttr(n,v)
24685         {
24686             
24687             if (v.match(/^\./) || v.match(/^\//)) {
24688                 return;
24689             }
24690             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24691                 return;
24692             }
24693             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24694             node.removeAttribute(n);
24695             
24696         }
24697         
24698         function cleanStyle(n,v)
24699         {
24700             if (v.match(/expression/)) { //XSS?? should we even bother..
24701                 node.removeAttribute(n);
24702                 return;
24703             }
24704             
24705             
24706             var parts = v.split(/;/);
24707             Roo.each(parts, function(p) {
24708                 p = p.replace(/\s+/g,'');
24709                 if (!p.length) {
24710                     return;
24711                 }
24712                 var l = p.split(':').shift().replace(/\s+/g,'');
24713                 
24714                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24715                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24716                     node.removeAttribute(n);
24717                     return false;
24718                 }
24719             });
24720             
24721             
24722         }
24723         
24724         
24725         for (var i = node.attributes.length-1; i > -1 ; i--) {
24726             var a = node.attributes[i];
24727             //console.log(a);
24728             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24729                 node.removeAttribute(a.name);
24730                 return;
24731             }
24732             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24733                 cleanAttr(a.name,a.value); // fixme..
24734                 return;
24735             }
24736             if (a.name == 'style') {
24737                 cleanStyle(a.name,a.value);
24738             }
24739             /// clean up MS crap..
24740             if (a.name == 'class') {
24741                 if (a.value.match(/^Mso/)) {
24742                     node.className = '';
24743                 }
24744             }
24745             
24746             // style cleanup!?
24747             // class cleanup?
24748             
24749         }
24750         
24751         
24752         this.cleanUpChildren(node);
24753         
24754         
24755     }
24756     
24757     
24758     // hide stuff that is not compatible
24759     /**
24760      * @event blur
24761      * @hide
24762      */
24763     /**
24764      * @event change
24765      * @hide
24766      */
24767     /**
24768      * @event focus
24769      * @hide
24770      */
24771     /**
24772      * @event specialkey
24773      * @hide
24774      */
24775     /**
24776      * @cfg {String} fieldClass @hide
24777      */
24778     /**
24779      * @cfg {String} focusClass @hide
24780      */
24781     /**
24782      * @cfg {String} autoCreate @hide
24783      */
24784     /**
24785      * @cfg {String} inputType @hide
24786      */
24787     /**
24788      * @cfg {String} invalidClass @hide
24789      */
24790     /**
24791      * @cfg {String} invalidText @hide
24792      */
24793     /**
24794      * @cfg {String} msgFx @hide
24795      */
24796     /**
24797      * @cfg {String} validateOnBlur @hide
24798      */
24799 });
24800
24801 Roo.form.HtmlEditor.white = [
24802         'area', 'br', 'img', 'input', 'hr', 'wbr',
24803         
24804        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24805        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24806        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24807        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24808        'table',   'ul',         'xmp', 
24809        
24810        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24811       'thead',   'tr', 
24812      
24813       'dir', 'menu', 'ol', 'ul', 'dl',
24814        
24815       'embed',  'object'
24816 ];
24817
24818
24819 Roo.form.HtmlEditor.black = [
24820     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24821         'applet', // 
24822         'base',   'basefont', 'bgsound', 'blink',  'body', 
24823         'frame',  'frameset', 'head',    'html',   'ilayer', 
24824         'iframe', 'layer',  'link',     'meta',    'object',   
24825         'script', 'style' ,'title',  'xml' // clean later..
24826 ];
24827 Roo.form.HtmlEditor.clean = [
24828     'script', 'style', 'title', 'xml'
24829 ];
24830
24831 // attributes..
24832
24833 Roo.form.HtmlEditor.ablack = [
24834     'on'
24835 ];
24836     
24837 Roo.form.HtmlEditor.aclean = [ 
24838     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24839 ];
24840
24841 // protocols..
24842 Roo.form.HtmlEditor.pwhite= [
24843         'http',  'https',  'mailto'
24844 ];
24845
24846 Roo.form.HtmlEditor.cwhite= [
24847         'text-align',
24848         'font-size'
24849 ];
24850
24851 // <script type="text/javascript">
24852 /*
24853  * Based on
24854  * Ext JS Library 1.1.1
24855  * Copyright(c) 2006-2007, Ext JS, LLC.
24856  *  
24857  
24858  */
24859
24860 /**
24861  * @class Roo.form.HtmlEditorToolbar1
24862  * Basic Toolbar
24863  * 
24864  * Usage:
24865  *
24866  new Roo.form.HtmlEditor({
24867     ....
24868     toolbars : [
24869         new Roo.form.HtmlEditorToolbar1({
24870             disable : { fonts: 1 , format: 1, ..., ... , ...],
24871             btns : [ .... ]
24872         })
24873     }
24874      
24875  * 
24876  * @cfg {Object} disable List of elements to disable..
24877  * @cfg {Array} btns List of additional buttons.
24878  * 
24879  * 
24880  * NEEDS Extra CSS? 
24881  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24882  */
24883  
24884 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24885 {
24886     
24887     Roo.apply(this, config);
24888     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24889     // dont call parent... till later.
24890 }
24891
24892 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24893     
24894     tb: false,
24895     
24896     rendered: false,
24897     
24898     editor : false,
24899     /**
24900      * @cfg {Object} disable  List of toolbar elements to disable
24901          
24902      */
24903     disable : false,
24904       /**
24905      * @cfg {Array} fontFamilies An array of available font families
24906      */
24907     fontFamilies : [
24908         'Arial',
24909         'Courier New',
24910         'Tahoma',
24911         'Times New Roman',
24912         'Verdana'
24913     ],
24914     
24915     specialChars : [
24916            "&#169;",
24917           "&#174;",     
24918           "&#8482;",    
24919           "&#163;" ,    
24920          // "&#8212;",    
24921           "&#8230;",    
24922           "&#247;" ,    
24923         //  "&#225;" ,     ?? a acute?
24924            "&#8364;"    , //Euro
24925        //   "&#8220;"    ,
24926         //  "&#8221;"    ,
24927         //  "&#8226;"    ,
24928           "&#176;"  //   , // degrees
24929
24930          // "&#233;"     , // e ecute
24931          // "&#250;"     , // u ecute?
24932     ],
24933     inputElements : [ 
24934             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24935             "input:submit", "input:button", "select", "textarea", "label" ],
24936     formats : [
24937         ["p"] ,  
24938         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24939         ["pre"],[ "code"], 
24940         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24941     ],
24942      /**
24943      * @cfg {String} defaultFont default font to use.
24944      */
24945     defaultFont: 'tahoma',
24946    
24947     fontSelect : false,
24948     
24949     
24950     formatCombo : false,
24951     
24952     init : function(editor)
24953     {
24954         this.editor = editor;
24955         
24956         
24957         var fid = editor.frameId;
24958         var etb = this;
24959         function btn(id, toggle, handler){
24960             var xid = fid + '-'+ id ;
24961             return {
24962                 id : xid,
24963                 cmd : id,
24964                 cls : 'x-btn-icon x-edit-'+id,
24965                 enableToggle:toggle !== false,
24966                 scope: editor, // was editor...
24967                 handler:handler||editor.relayBtnCmd,
24968                 clickEvent:'mousedown',
24969                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24970                 tabIndex:-1
24971             };
24972         }
24973         
24974         
24975         
24976         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24977         this.tb = tb;
24978          // stop form submits
24979         tb.el.on('click', function(e){
24980             e.preventDefault(); // what does this do?
24981         });
24982
24983         if(!this.disable.font && !Roo.isSafari){
24984             /* why no safari for fonts
24985             editor.fontSelect = tb.el.createChild({
24986                 tag:'select',
24987                 tabIndex: -1,
24988                 cls:'x-font-select',
24989                 html: editor.createFontOptions()
24990             });
24991             editor.fontSelect.on('change', function(){
24992                 var font = editor.fontSelect.dom.value;
24993                 editor.relayCmd('fontname', font);
24994                 editor.deferFocus();
24995             }, editor);
24996             tb.add(
24997                 editor.fontSelect.dom,
24998                 '-'
24999             );
25000             */
25001         };
25002         if(!this.disable.formats){
25003             this.formatCombo = new Roo.form.ComboBox({
25004                 store: new Roo.data.SimpleStore({
25005                     id : 'tag',
25006                     fields: ['tag'],
25007                     data : this.formats // from states.js
25008                 }),
25009                 blockFocus : true,
25010                 //autoCreate : {tag: "div",  size: "20"},
25011                 displayField:'tag',
25012                 typeAhead: false,
25013                 mode: 'local',
25014                 editable : false,
25015                 triggerAction: 'all',
25016                 emptyText:'Add tag',
25017                 selectOnFocus:true,
25018                 width:135,
25019                 listeners : {
25020                     'select': function(c, r, i) {
25021                         editor.insertTag(r.get('tag'));
25022                         editor.focus();
25023                     }
25024                 }
25025
25026             });
25027             tb.addField(this.formatCombo);
25028             
25029         }
25030         
25031         if(!this.disable.format){
25032             tb.add(
25033                 btn('bold'),
25034                 btn('italic'),
25035                 btn('underline')
25036             );
25037         };
25038         if(!this.disable.fontSize){
25039             tb.add(
25040                 '-',
25041                 
25042                 
25043                 btn('increasefontsize', false, editor.adjustFont),
25044                 btn('decreasefontsize', false, editor.adjustFont)
25045             );
25046         };
25047         
25048         
25049         if(this.disable.colors){
25050             tb.add(
25051                 '-', {
25052                     id:editor.frameId +'-forecolor',
25053                     cls:'x-btn-icon x-edit-forecolor',
25054                     clickEvent:'mousedown',
25055                     tooltip: this.buttonTips['forecolor'] || undefined,
25056                     tabIndex:-1,
25057                     menu : new Roo.menu.ColorMenu({
25058                         allowReselect: true,
25059                         focus: Roo.emptyFn,
25060                         value:'000000',
25061                         plain:true,
25062                         selectHandler: function(cp, color){
25063                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25064                             editor.deferFocus();
25065                         },
25066                         scope: editor,
25067                         clickEvent:'mousedown'
25068                     })
25069                 }, {
25070                     id:editor.frameId +'backcolor',
25071                     cls:'x-btn-icon x-edit-backcolor',
25072                     clickEvent:'mousedown',
25073                     tooltip: this.buttonTips['backcolor'] || undefined,
25074                     tabIndex:-1,
25075                     menu : new Roo.menu.ColorMenu({
25076                         focus: Roo.emptyFn,
25077                         value:'FFFFFF',
25078                         plain:true,
25079                         allowReselect: true,
25080                         selectHandler: function(cp, color){
25081                             if(Roo.isGecko){
25082                                 editor.execCmd('useCSS', false);
25083                                 editor.execCmd('hilitecolor', color);
25084                                 editor.execCmd('useCSS', true);
25085                                 editor.deferFocus();
25086                             }else{
25087                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25088                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25089                                 editor.deferFocus();
25090                             }
25091                         },
25092                         scope:editor,
25093                         clickEvent:'mousedown'
25094                     })
25095                 }
25096             );
25097         };
25098         // now add all the items...
25099         
25100
25101         if(!this.disable.alignments){
25102             tb.add(
25103                 '-',
25104                 btn('justifyleft'),
25105                 btn('justifycenter'),
25106                 btn('justifyright')
25107             );
25108         };
25109
25110         //if(!Roo.isSafari){
25111             if(!this.disable.links){
25112                 tb.add(
25113                     '-',
25114                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25115                 );
25116             };
25117
25118             if(!this.disable.lists){
25119                 tb.add(
25120                     '-',
25121                     btn('insertorderedlist'),
25122                     btn('insertunorderedlist')
25123                 );
25124             }
25125             if(!this.disable.sourceEdit){
25126                 tb.add(
25127                     '-',
25128                     btn('sourceedit', true, function(btn){
25129                         this.toggleSourceEdit(btn.pressed);
25130                     })
25131                 );
25132             }
25133         //}
25134         
25135         var smenu = { };
25136         // special menu.. - needs to be tidied up..
25137         if (!this.disable.special) {
25138             smenu = {
25139                 text: "&#169;",
25140                 cls: 'x-edit-none',
25141                 menu : {
25142                     items : []
25143                    }
25144             };
25145             for (var i =0; i < this.specialChars.length; i++) {
25146                 smenu.menu.items.push({
25147                     
25148                     html: this.specialChars[i],
25149                     handler: function(a,b) {
25150                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25151                         
25152                     },
25153                     tabIndex:-1
25154                 });
25155             }
25156             
25157             
25158             tb.add(smenu);
25159             
25160             
25161         }
25162         if (this.btns) {
25163             for(var i =0; i< this.btns.length;i++) {
25164                 var b = this.btns[i];
25165                 b.cls =  'x-edit-none';
25166                 b.scope = editor;
25167                 tb.add(b);
25168             }
25169         
25170         }
25171         
25172         
25173         
25174         // disable everything...
25175         
25176         this.tb.items.each(function(item){
25177            if(item.id != editor.frameId+ '-sourceedit'){
25178                 item.disable();
25179             }
25180         });
25181         this.rendered = true;
25182         
25183         // the all the btns;
25184         editor.on('editorevent', this.updateToolbar, this);
25185         // other toolbars need to implement this..
25186         //editor.on('editmodechange', this.updateToolbar, this);
25187     },
25188     
25189     
25190     
25191     /**
25192      * Protected method that will not generally be called directly. It triggers
25193      * a toolbar update by reading the markup state of the current selection in the editor.
25194      */
25195     updateToolbar: function(){
25196
25197         if(!this.editor.activated){
25198             this.editor.onFirstFocus();
25199             return;
25200         }
25201
25202         var btns = this.tb.items.map, 
25203             doc = this.editor.doc,
25204             frameId = this.editor.frameId;
25205
25206         if(!this.disable.font && !Roo.isSafari){
25207             /*
25208             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25209             if(name != this.fontSelect.dom.value){
25210                 this.fontSelect.dom.value = name;
25211             }
25212             */
25213         }
25214         if(!this.disable.format){
25215             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25216             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25217             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25218         }
25219         if(!this.disable.alignments){
25220             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25221             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25222             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25223         }
25224         if(!Roo.isSafari && !this.disable.lists){
25225             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25226             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25227         }
25228         
25229         var ans = this.editor.getAllAncestors();
25230         if (this.formatCombo) {
25231             
25232             
25233             var store = this.formatCombo.store;
25234             this.formatCombo.setValue("");
25235             for (var i =0; i < ans.length;i++) {
25236                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25237                     // select it..
25238                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25239                     break;
25240                 }
25241             }
25242         }
25243         
25244         
25245         
25246         // hides menus... - so this cant be on a menu...
25247         Roo.menu.MenuMgr.hideAll();
25248
25249         //this.editorsyncValue();
25250     },
25251    
25252     
25253     createFontOptions : function(){
25254         var buf = [], fs = this.fontFamilies, ff, lc;
25255         for(var i = 0, len = fs.length; i< len; i++){
25256             ff = fs[i];
25257             lc = ff.toLowerCase();
25258             buf.push(
25259                 '<option value="',lc,'" style="font-family:',ff,';"',
25260                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25261                     ff,
25262                 '</option>'
25263             );
25264         }
25265         return buf.join('');
25266     },
25267     
25268     toggleSourceEdit : function(sourceEditMode){
25269         if(sourceEditMode === undefined){
25270             sourceEditMode = !this.sourceEditMode;
25271         }
25272         this.sourceEditMode = sourceEditMode === true;
25273         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25274         // just toggle the button?
25275         if(btn.pressed !== this.editor.sourceEditMode){
25276             btn.toggle(this.editor.sourceEditMode);
25277             return;
25278         }
25279         
25280         if(this.sourceEditMode){
25281             this.tb.items.each(function(item){
25282                 if(item.cmd != 'sourceedit'){
25283                     item.disable();
25284                 }
25285             });
25286           
25287         }else{
25288             if(this.initialized){
25289                 this.tb.items.each(function(item){
25290                     item.enable();
25291                 });
25292             }
25293             
25294         }
25295         // tell the editor that it's been pressed..
25296         this.editor.toggleSourceEdit(sourceEditMode);
25297        
25298     },
25299      /**
25300      * Object collection of toolbar tooltips for the buttons in the editor. The key
25301      * is the command id associated with that button and the value is a valid QuickTips object.
25302      * For example:
25303 <pre><code>
25304 {
25305     bold : {
25306         title: 'Bold (Ctrl+B)',
25307         text: 'Make the selected text bold.',
25308         cls: 'x-html-editor-tip'
25309     },
25310     italic : {
25311         title: 'Italic (Ctrl+I)',
25312         text: 'Make the selected text italic.',
25313         cls: 'x-html-editor-tip'
25314     },
25315     ...
25316 </code></pre>
25317     * @type Object
25318      */
25319     buttonTips : {
25320         bold : {
25321             title: 'Bold (Ctrl+B)',
25322             text: 'Make the selected text bold.',
25323             cls: 'x-html-editor-tip'
25324         },
25325         italic : {
25326             title: 'Italic (Ctrl+I)',
25327             text: 'Make the selected text italic.',
25328             cls: 'x-html-editor-tip'
25329         },
25330         underline : {
25331             title: 'Underline (Ctrl+U)',
25332             text: 'Underline the selected text.',
25333             cls: 'x-html-editor-tip'
25334         },
25335         increasefontsize : {
25336             title: 'Grow Text',
25337             text: 'Increase the font size.',
25338             cls: 'x-html-editor-tip'
25339         },
25340         decreasefontsize : {
25341             title: 'Shrink Text',
25342             text: 'Decrease the font size.',
25343             cls: 'x-html-editor-tip'
25344         },
25345         backcolor : {
25346             title: 'Text Highlight Color',
25347             text: 'Change the background color of the selected text.',
25348             cls: 'x-html-editor-tip'
25349         },
25350         forecolor : {
25351             title: 'Font Color',
25352             text: 'Change the color of the selected text.',
25353             cls: 'x-html-editor-tip'
25354         },
25355         justifyleft : {
25356             title: 'Align Text Left',
25357             text: 'Align text to the left.',
25358             cls: 'x-html-editor-tip'
25359         },
25360         justifycenter : {
25361             title: 'Center Text',
25362             text: 'Center text in the editor.',
25363             cls: 'x-html-editor-tip'
25364         },
25365         justifyright : {
25366             title: 'Align Text Right',
25367             text: 'Align text to the right.',
25368             cls: 'x-html-editor-tip'
25369         },
25370         insertunorderedlist : {
25371             title: 'Bullet List',
25372             text: 'Start a bulleted list.',
25373             cls: 'x-html-editor-tip'
25374         },
25375         insertorderedlist : {
25376             title: 'Numbered List',
25377             text: 'Start a numbered list.',
25378             cls: 'x-html-editor-tip'
25379         },
25380         createlink : {
25381             title: 'Hyperlink',
25382             text: 'Make the selected text a hyperlink.',
25383             cls: 'x-html-editor-tip'
25384         },
25385         sourceedit : {
25386             title: 'Source Edit',
25387             text: 'Switch to source editing mode.',
25388             cls: 'x-html-editor-tip'
25389         }
25390     },
25391     // private
25392     onDestroy : function(){
25393         if(this.rendered){
25394             
25395             this.tb.items.each(function(item){
25396                 if(item.menu){
25397                     item.menu.removeAll();
25398                     if(item.menu.el){
25399                         item.menu.el.destroy();
25400                     }
25401                 }
25402                 item.destroy();
25403             });
25404              
25405         }
25406     },
25407     onFirstFocus: function() {
25408         this.tb.items.each(function(item){
25409            item.enable();
25410         });
25411     }
25412 });
25413
25414
25415
25416
25417 // <script type="text/javascript">
25418 /*
25419  * Based on
25420  * Ext JS Library 1.1.1
25421  * Copyright(c) 2006-2007, Ext JS, LLC.
25422  *  
25423  
25424  */
25425
25426  
25427 /**
25428  * @class Roo.form.HtmlEditor.ToolbarContext
25429  * Context Toolbar
25430  * 
25431  * Usage:
25432  *
25433  new Roo.form.HtmlEditor({
25434     ....
25435     toolbars : [
25436         new Roo.form.HtmlEditor.ToolbarStandard(),
25437         new Roo.form.HtmlEditor.ToolbarContext()
25438         })
25439     }
25440      
25441  * 
25442  * @config : {Object} disable List of elements to disable.. (not done yet.)
25443  * 
25444  * 
25445  */
25446
25447 Roo.form.HtmlEditor.ToolbarContext = function(config)
25448 {
25449     
25450     Roo.apply(this, config);
25451     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25452     // dont call parent... till later.
25453 }
25454 Roo.form.HtmlEditor.ToolbarContext.types = {
25455     'IMG' : {
25456         width : {
25457             title: "Width",
25458             width: 40
25459         },
25460         height:  {
25461             title: "Height",
25462             width: 40
25463         },
25464         align: {
25465             title: "Align",
25466             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25467             width : 80
25468             
25469         },
25470         border: {
25471             title: "Border",
25472             width: 40
25473         },
25474         alt: {
25475             title: "Alt",
25476             width: 120
25477         },
25478         src : {
25479             title: "Src",
25480             width: 220
25481         }
25482         
25483     },
25484     'A' : {
25485         name : {
25486             title: "Name",
25487             width: 50
25488         },
25489         href:  {
25490             title: "Href",
25491             width: 220
25492         } // border?
25493         
25494     },
25495     'TABLE' : {
25496         rows : {
25497             title: "Rows",
25498             width: 20
25499         },
25500         cols : {
25501             title: "Cols",
25502             width: 20
25503         },
25504         width : {
25505             title: "Width",
25506             width: 40
25507         },
25508         height : {
25509             title: "Height",
25510             width: 40
25511         },
25512         border : {
25513             title: "Border",
25514             width: 20
25515         }
25516     },
25517     'TD' : {
25518         width : {
25519             title: "Width",
25520             width: 40
25521         },
25522         height : {
25523             title: "Height",
25524             width: 40
25525         },   
25526         align: {
25527             title: "Align",
25528             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25529             width: 40
25530         },
25531         valign: {
25532             title: "Valign",
25533             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25534             width: 40
25535         },
25536         colspan: {
25537             title: "Colspan",
25538             width: 20
25539             
25540         }
25541     },
25542     'INPUT' : {
25543         name : {
25544             title: "name",
25545             width: 120
25546         },
25547         value : {
25548             title: "Value",
25549             width: 120
25550         },
25551         width : {
25552             title: "Width",
25553             width: 40
25554         }
25555     },
25556     'LABEL' : {
25557         'for' : {
25558             title: "For",
25559             width: 120
25560         }
25561     },
25562     'TEXTAREA' : {
25563           name : {
25564             title: "name",
25565             width: 120
25566         },
25567         rows : {
25568             title: "Rows",
25569             width: 20
25570         },
25571         cols : {
25572             title: "Cols",
25573             width: 20
25574         }
25575     },
25576     'SELECT' : {
25577         name : {
25578             title: "name",
25579             width: 120
25580         },
25581         selectoptions : {
25582             title: "Options",
25583             width: 200
25584         }
25585     },
25586     'BODY' : {
25587         title : {
25588             title: "title",
25589             width: 120,
25590             disabled : true
25591         }
25592     }
25593 };
25594
25595
25596
25597 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25598     
25599     tb: false,
25600     
25601     rendered: false,
25602     
25603     editor : false,
25604     /**
25605      * @cfg {Object} disable  List of toolbar elements to disable
25606          
25607      */
25608     disable : false,
25609     
25610     
25611     
25612     toolbars : false,
25613     
25614     init : function(editor)
25615     {
25616         this.editor = editor;
25617         
25618         
25619         var fid = editor.frameId;
25620         var etb = this;
25621         function btn(id, toggle, handler){
25622             var xid = fid + '-'+ id ;
25623             return {
25624                 id : xid,
25625                 cmd : id,
25626                 cls : 'x-btn-icon x-edit-'+id,
25627                 enableToggle:toggle !== false,
25628                 scope: editor, // was editor...
25629                 handler:handler||editor.relayBtnCmd,
25630                 clickEvent:'mousedown',
25631                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25632                 tabIndex:-1
25633             };
25634         }
25635         // create a new element.
25636         var wdiv = editor.wrap.createChild({
25637                 tag: 'div'
25638             }, editor.wrap.dom.firstChild.nextSibling, true);
25639         
25640         // can we do this more than once??
25641         
25642          // stop form submits
25643       
25644  
25645         // disable everything...
25646         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25647         this.toolbars = {};
25648            
25649         for (var i in  ty) {
25650           
25651             this.toolbars[i] = this.buildToolbar(ty[i],i);
25652         }
25653         this.tb = this.toolbars.BODY;
25654         this.tb.el.show();
25655         
25656          
25657         this.rendered = true;
25658         
25659         // the all the btns;
25660         editor.on('editorevent', this.updateToolbar, this);
25661         // other toolbars need to implement this..
25662         //editor.on('editmodechange', this.updateToolbar, this);
25663     },
25664     
25665     
25666     
25667     /**
25668      * Protected method that will not generally be called directly. It triggers
25669      * a toolbar update by reading the markup state of the current selection in the editor.
25670      */
25671     updateToolbar: function(){
25672
25673         if(!this.editor.activated){
25674             this.editor.onFirstFocus();
25675             return;
25676         }
25677
25678         
25679         var ans = this.editor.getAllAncestors();
25680         
25681         // pick
25682         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25683         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25684         sel = sel ? sel : this.editor.doc.body;
25685         sel = sel.tagName.length ? sel : this.editor.doc.body;
25686         var tn = sel.tagName.toUpperCase();
25687         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25688         tn = sel.tagName.toUpperCase();
25689         if (this.tb.name  == tn) {
25690             return; // no change
25691         }
25692         this.tb.el.hide();
25693         ///console.log("show: " + tn);
25694         this.tb =  this.toolbars[tn];
25695         this.tb.el.show();
25696         this.tb.fields.each(function(e) {
25697             e.setValue(sel.getAttribute(e.name));
25698         });
25699         this.tb.selectedNode = sel;
25700         
25701         
25702         Roo.menu.MenuMgr.hideAll();
25703
25704         //this.editorsyncValue();
25705     },
25706    
25707        
25708     // private
25709     onDestroy : function(){
25710         if(this.rendered){
25711             
25712             this.tb.items.each(function(item){
25713                 if(item.menu){
25714                     item.menu.removeAll();
25715                     if(item.menu.el){
25716                         item.menu.el.destroy();
25717                     }
25718                 }
25719                 item.destroy();
25720             });
25721              
25722         }
25723     },
25724     onFirstFocus: function() {
25725         // need to do this for all the toolbars..
25726         this.tb.items.each(function(item){
25727            item.enable();
25728         });
25729     },
25730     buildToolbar: function(tlist, nm)
25731     {
25732         var editor = this.editor;
25733          // create a new element.
25734         var wdiv = editor.wrap.createChild({
25735                 tag: 'div'
25736             }, editor.wrap.dom.firstChild.nextSibling, true);
25737         
25738        
25739         var tb = new Roo.Toolbar(wdiv);
25740         tb.add(nm+ ":&nbsp;");
25741         for (var i in tlist) {
25742             var item = tlist[i];
25743             tb.add(item.title + ":&nbsp;");
25744             if (item.opts) {
25745                 // fixme
25746                 
25747               
25748                 tb.addField( new Roo.form.ComboBox({
25749                     store: new Roo.data.SimpleStore({
25750                         id : 'val',
25751                         fields: ['val'],
25752                         data : item.opts // from states.js
25753                     }),
25754                     name : i,
25755                     displayField:'val',
25756                     typeAhead: false,
25757                     mode: 'local',
25758                     editable : false,
25759                     triggerAction: 'all',
25760                     emptyText:'Select',
25761                     selectOnFocus:true,
25762                     width: item.width ? item.width  : 130,
25763                     listeners : {
25764                         'select': function(c, r, i) {
25765                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25766                         }
25767                     }
25768
25769                 }));
25770                 continue;
25771                     
25772                 
25773                 
25774                 
25775                 
25776                 tb.addField( new Roo.form.TextField({
25777                     name: i,
25778                     width: 100,
25779                     //allowBlank:false,
25780                     value: ''
25781                 }));
25782                 continue;
25783             }
25784             tb.addField( new Roo.form.TextField({
25785                 name: i,
25786                 width: item.width,
25787                 //allowBlank:true,
25788                 value: '',
25789                 listeners: {
25790                     'change' : function(f, nv, ov) {
25791                         tb.selectedNode.setAttribute(f.name, nv);
25792                     }
25793                 }
25794             }));
25795              
25796         }
25797         tb.el.on('click', function(e){
25798             e.preventDefault(); // what does this do?
25799         });
25800         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25801         tb.el.hide();
25802         tb.name = nm;
25803         // dont need to disable them... as they will get hidden
25804         return tb;
25805          
25806         
25807     }
25808     
25809     
25810     
25811     
25812 });
25813
25814
25815
25816
25817
25818 /*
25819  * Based on:
25820  * Ext JS Library 1.1.1
25821  * Copyright(c) 2006-2007, Ext JS, LLC.
25822  *
25823  * Originally Released Under LGPL - original licence link has changed is not relivant.
25824  *
25825  * Fork - LGPL
25826  * <script type="text/javascript">
25827  */
25828  
25829 /**
25830  * @class Roo.form.BasicForm
25831  * @extends Roo.util.Observable
25832  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25833  * @constructor
25834  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25835  * @param {Object} config Configuration options
25836  */
25837 Roo.form.BasicForm = function(el, config){
25838     this.allItems = [];
25839     this.childForms = [];
25840     Roo.apply(this, config);
25841     /*
25842      * The Roo.form.Field items in this form.
25843      * @type MixedCollection
25844      */
25845      
25846      
25847     this.items = new Roo.util.MixedCollection(false, function(o){
25848         return o.id || (o.id = Roo.id());
25849     });
25850     this.addEvents({
25851         /**
25852          * @event beforeaction
25853          * Fires before any action is performed. Return false to cancel the action.
25854          * @param {Form} this
25855          * @param {Action} action The action to be performed
25856          */
25857         beforeaction: true,
25858         /**
25859          * @event actionfailed
25860          * Fires when an action fails.
25861          * @param {Form} this
25862          * @param {Action} action The action that failed
25863          */
25864         actionfailed : true,
25865         /**
25866          * @event actioncomplete
25867          * Fires when an action is completed.
25868          * @param {Form} this
25869          * @param {Action} action The action that completed
25870          */
25871         actioncomplete : true
25872     });
25873     if(el){
25874         this.initEl(el);
25875     }
25876     Roo.form.BasicForm.superclass.constructor.call(this);
25877 };
25878
25879 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25880     /**
25881      * @cfg {String} method
25882      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25883      */
25884     /**
25885      * @cfg {DataReader} reader
25886      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25887      * This is optional as there is built-in support for processing JSON.
25888      */
25889     /**
25890      * @cfg {DataReader} errorReader
25891      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25892      * This is completely optional as there is built-in support for processing JSON.
25893      */
25894     /**
25895      * @cfg {String} url
25896      * The URL to use for form actions if one isn't supplied in the action options.
25897      */
25898     /**
25899      * @cfg {Boolean} fileUpload
25900      * Set to true if this form is a file upload.
25901      */
25902     /**
25903      * @cfg {Object} baseParams
25904      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25905      */
25906     /**
25907      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25908      */
25909     timeout: 30,
25910
25911     // private
25912     activeAction : null,
25913
25914     /**
25915      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25916      * or setValues() data instead of when the form was first created.
25917      */
25918     trackResetOnLoad : false,
25919     
25920     
25921     /**
25922      * childForms - used for multi-tab forms
25923      * @type {Array}
25924      */
25925     childForms : false,
25926     
25927     /**
25928      * allItems - full list of fields.
25929      * @type {Array}
25930      */
25931     allItems : false,
25932     
25933     /**
25934      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
25935      * element by passing it or its id or mask the form itself by passing in true.
25936      * @type Mixed
25937      */
25938     waitMsgTarget : undefined,
25939
25940     // private
25941     initEl : function(el){
25942         this.el = Roo.get(el);
25943         this.id = this.el.id || Roo.id();
25944         this.el.on('submit', this.onSubmit, this);
25945         this.el.addClass('x-form');
25946     },
25947
25948     // private
25949     onSubmit : function(e){
25950         e.stopEvent();
25951     },
25952
25953     /**
25954      * Returns true if client-side validation on the form is successful.
25955      * @return Boolean
25956      */
25957     isValid : function(){
25958         var valid = true;
25959         this.items.each(function(f){
25960            if(!f.validate()){
25961                valid = false;
25962            }
25963         });
25964         return valid;
25965     },
25966
25967     /**
25968      * Returns true if any fields in this form have changed since their original load.
25969      * @return Boolean
25970      */
25971     isDirty : function(){
25972         var dirty = false;
25973         this.items.each(function(f){
25974            if(f.isDirty()){
25975                dirty = true;
25976                return false;
25977            }
25978         });
25979         return dirty;
25980     },
25981
25982     /**
25983      * Performs a predefined action (submit or load) or custom actions you define on this form.
25984      * @param {String} actionName The name of the action type
25985      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25986      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25987      * accept other config options):
25988      * <pre>
25989 Property          Type             Description
25990 ----------------  ---------------  ----------------------------------------------------------------------------------
25991 url               String           The url for the action (defaults to the form's url)
25992 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25993 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25994 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25995                                    validate the form on the client (defaults to false)
25996      * </pre>
25997      * @return {BasicForm} this
25998      */
25999     doAction : function(action, options){
26000         if(typeof action == 'string'){
26001             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26002         }
26003         if(this.fireEvent('beforeaction', this, action) !== false){
26004             this.beforeAction(action);
26005             action.run.defer(100, action);
26006         }
26007         return this;
26008     },
26009
26010     /**
26011      * Shortcut to do a submit action.
26012      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26013      * @return {BasicForm} this
26014      */
26015     submit : function(options){
26016         this.doAction('submit', options);
26017         return this;
26018     },
26019
26020     /**
26021      * Shortcut to do a load action.
26022      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26023      * @return {BasicForm} this
26024      */
26025     load : function(options){
26026         this.doAction('load', options);
26027         return this;
26028     },
26029
26030     /**
26031      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26032      * @param {Record} record The record to edit
26033      * @return {BasicForm} this
26034      */
26035     updateRecord : function(record){
26036         record.beginEdit();
26037         var fs = record.fields;
26038         fs.each(function(f){
26039             var field = this.findField(f.name);
26040             if(field){
26041                 record.set(f.name, field.getValue());
26042             }
26043         }, this);
26044         record.endEdit();
26045         return this;
26046     },
26047
26048     /**
26049      * Loads an Roo.data.Record into this form.
26050      * @param {Record} record The record to load
26051      * @return {BasicForm} this
26052      */
26053     loadRecord : function(record){
26054         this.setValues(record.data);
26055         return this;
26056     },
26057
26058     // private
26059     beforeAction : function(action){
26060         var o = action.options;
26061         if(o.waitMsg){
26062             if(this.waitMsgTarget === true){
26063                 this.el.mask(o.waitMsg, 'x-mask-loading');
26064             }else if(this.waitMsgTarget){
26065                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26066                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26067             }else{
26068                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26069             }
26070         }
26071     },
26072
26073     // private
26074     afterAction : function(action, success){
26075         this.activeAction = null;
26076         var o = action.options;
26077         if(o.waitMsg){
26078             if(this.waitMsgTarget === true){
26079                 this.el.unmask();
26080             }else if(this.waitMsgTarget){
26081                 this.waitMsgTarget.unmask();
26082             }else{
26083                 Roo.MessageBox.updateProgress(1);
26084                 Roo.MessageBox.hide();
26085             }
26086         }
26087         if(success){
26088             if(o.reset){
26089                 this.reset();
26090             }
26091             Roo.callback(o.success, o.scope, [this, action]);
26092             this.fireEvent('actioncomplete', this, action);
26093         }else{
26094             Roo.callback(o.failure, o.scope, [this, action]);
26095             this.fireEvent('actionfailed', this, action);
26096         }
26097     },
26098
26099     /**
26100      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26101      * @param {String} id The value to search for
26102      * @return Field
26103      */
26104     findField : function(id){
26105         var field = this.items.get(id);
26106         if(!field){
26107             this.items.each(function(f){
26108                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26109                     field = f;
26110                     return false;
26111                 }
26112             });
26113         }
26114         return field || null;
26115     },
26116
26117     /**
26118      * Add a secondary form to this one, 
26119      * Used to provide tabbed forms. One form is primary, with hidden values 
26120      * which mirror the elements from the other forms.
26121      * 
26122      * @param {Roo.form.Form} form to add.
26123      * 
26124      */
26125     addForm : function(form)
26126     {
26127        
26128         if (this.childForms.indexOf(form) > -1) {
26129             // already added..
26130             return;
26131         }
26132         this.childForms.push(form);
26133         var n = '';
26134         Roo.each(form.allItems, function (fe) {
26135             
26136             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26137             if (this.findField(n)) { // already added..
26138                 return;
26139             }
26140             var add = new Roo.form.Hidden({
26141                 name : n
26142             });
26143             add.render(this.el);
26144             
26145             this.add( add );
26146         }, this);
26147         
26148     },
26149     /**
26150      * Mark fields in this form invalid in bulk.
26151      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26152      * @return {BasicForm} this
26153      */
26154     markInvalid : function(errors){
26155         if(errors instanceof Array){
26156             for(var i = 0, len = errors.length; i < len; i++){
26157                 var fieldError = errors[i];
26158                 var f = this.findField(fieldError.id);
26159                 if(f){
26160                     f.markInvalid(fieldError.msg);
26161                 }
26162             }
26163         }else{
26164             var field, id;
26165             for(id in errors){
26166                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26167                     field.markInvalid(errors[id]);
26168                 }
26169             }
26170         }
26171         Roo.each(this.childForms || [], function (f) {
26172             f.markInvalid(errors);
26173         });
26174         
26175         return this;
26176     },
26177
26178     /**
26179      * Set values for fields in this form in bulk.
26180      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26181      * @return {BasicForm} this
26182      */
26183     setValues : function(values){
26184         if(values instanceof Array){ // array of objects
26185             for(var i = 0, len = values.length; i < len; i++){
26186                 var v = values[i];
26187                 var f = this.findField(v.id);
26188                 if(f){
26189                     f.setValue(v.value);
26190                     if(this.trackResetOnLoad){
26191                         f.originalValue = f.getValue();
26192                     }
26193                 }
26194             }
26195         }else{ // object hash
26196             var field, id;
26197             for(id in values){
26198                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26199                     
26200                     if (field.setFromData && 
26201                         field.valueField && 
26202                         field.displayField &&
26203                         // combos' with local stores can 
26204                         // be queried via setValue()
26205                         // to set their value..
26206                         (field.store && !field.store.isLocal)
26207                         ) {
26208                         // it's a combo
26209                         var sd = { };
26210                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26211                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26212                         field.setFromData(sd);
26213                         
26214                     } else {
26215                         field.setValue(values[id]);
26216                     }
26217                     
26218                     
26219                     if(this.trackResetOnLoad){
26220                         field.originalValue = field.getValue();
26221                     }
26222                 }
26223             }
26224         }
26225          
26226         Roo.each(this.childForms || [], function (f) {
26227             f.setValues(values);
26228         });
26229                 
26230         return this;
26231     },
26232
26233     /**
26234      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26235      * they are returned as an array.
26236      * @param {Boolean} asString
26237      * @return {Object}
26238      */
26239     getValues : function(asString){
26240         if (this.childForms) {
26241             // copy values from the child forms
26242             Roo.each(this.childForms, function (f) {
26243                 this.setValues(f.getValues());
26244             }, this);
26245         }
26246         
26247         
26248         
26249         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26250         if(asString === true){
26251             return fs;
26252         }
26253         return Roo.urlDecode(fs);
26254     },
26255     
26256     /**
26257      * Returns the fields in this form as an object with key/value pairs. 
26258      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26259      * @return {Object}
26260      */
26261     getFieldValues : function()
26262     {
26263         if (this.childForms) {
26264             // copy values from the child forms
26265             Roo.each(this.childForms, function (f) {
26266                 this.setValues(f.getValues());
26267             }, this);
26268         }
26269         
26270         var ret = {};
26271         this.items.each(function(f){
26272             if (!f.getName()) {
26273                 return;
26274             }
26275             var v = f.getValue();
26276             if ((typeof(v) == 'object') && f.getRawValue) {
26277                 v = f.getRawValue() ; // dates..
26278             }
26279             ret[f.getName()] = v;
26280         });
26281         
26282         return ret;
26283     },
26284
26285     /**
26286      * Clears all invalid messages in this form.
26287      * @return {BasicForm} this
26288      */
26289     clearInvalid : function(){
26290         this.items.each(function(f){
26291            f.clearInvalid();
26292         });
26293         
26294         Roo.each(this.childForms || [], function (f) {
26295             f.clearInvalid();
26296         });
26297         
26298         
26299         return this;
26300     },
26301
26302     /**
26303      * Resets this form.
26304      * @return {BasicForm} this
26305      */
26306     reset : function(){
26307         this.items.each(function(f){
26308             f.reset();
26309         });
26310         
26311         Roo.each(this.childForms || [], function (f) {
26312             f.reset();
26313         });
26314        
26315         
26316         return this;
26317     },
26318
26319     /**
26320      * Add Roo.form components to this form.
26321      * @param {Field} field1
26322      * @param {Field} field2 (optional)
26323      * @param {Field} etc (optional)
26324      * @return {BasicForm} this
26325      */
26326     add : function(){
26327         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26328         return this;
26329     },
26330
26331
26332     /**
26333      * Removes a field from the items collection (does NOT remove its markup).
26334      * @param {Field} field
26335      * @return {BasicForm} this
26336      */
26337     remove : function(field){
26338         this.items.remove(field);
26339         return this;
26340     },
26341
26342     /**
26343      * Looks at the fields in this form, checks them for an id attribute,
26344      * and calls applyTo on the existing dom element with that id.
26345      * @return {BasicForm} this
26346      */
26347     render : function(){
26348         this.items.each(function(f){
26349             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26350                 f.applyTo(f.id);
26351             }
26352         });
26353         return this;
26354     },
26355
26356     /**
26357      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26358      * @param {Object} values
26359      * @return {BasicForm} this
26360      */
26361     applyToFields : function(o){
26362         this.items.each(function(f){
26363            Roo.apply(f, o);
26364         });
26365         return this;
26366     },
26367
26368     /**
26369      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26370      * @param {Object} values
26371      * @return {BasicForm} this
26372      */
26373     applyIfToFields : function(o){
26374         this.items.each(function(f){
26375            Roo.applyIf(f, o);
26376         });
26377         return this;
26378     }
26379 });
26380
26381 // back compat
26382 Roo.BasicForm = Roo.form.BasicForm;/*
26383  * Based on:
26384  * Ext JS Library 1.1.1
26385  * Copyright(c) 2006-2007, Ext JS, LLC.
26386  *
26387  * Originally Released Under LGPL - original licence link has changed is not relivant.
26388  *
26389  * Fork - LGPL
26390  * <script type="text/javascript">
26391  */
26392
26393 /**
26394  * @class Roo.form.Form
26395  * @extends Roo.form.BasicForm
26396  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26397  * @constructor
26398  * @param {Object} config Configuration options
26399  */
26400 Roo.form.Form = function(config){
26401     var xitems =  [];
26402     if (config.items) {
26403         xitems = config.items;
26404         delete config.items;
26405     }
26406    
26407     
26408     Roo.form.Form.superclass.constructor.call(this, null, config);
26409     this.url = this.url || this.action;
26410     if(!this.root){
26411         this.root = new Roo.form.Layout(Roo.applyIf({
26412             id: Roo.id()
26413         }, config));
26414     }
26415     this.active = this.root;
26416     /**
26417      * Array of all the buttons that have been added to this form via {@link addButton}
26418      * @type Array
26419      */
26420     this.buttons = [];
26421     this.allItems = [];
26422     this.addEvents({
26423         /**
26424          * @event clientvalidation
26425          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26426          * @param {Form} this
26427          * @param {Boolean} valid true if the form has passed client-side validation
26428          */
26429         clientvalidation: true,
26430         /**
26431          * @event rendered
26432          * Fires when the form is rendered
26433          * @param {Roo.form.Form} form
26434          */
26435         rendered : true
26436     });
26437     
26438     if (this.progressUrl) {
26439             // push a hidden field onto the list of fields..
26440             this.addxtype( {
26441                     xns: Roo.form, 
26442                     xtype : 'Hidden', 
26443                     name : 'UPLOAD_IDENTIFIER' 
26444             });
26445         }
26446         
26447     
26448     Roo.each(xitems, this.addxtype, this);
26449     
26450     
26451     
26452 };
26453
26454 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26455     /**
26456      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26457      */
26458     /**
26459      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26460      */
26461     /**
26462      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26463      */
26464     buttonAlign:'center',
26465
26466     /**
26467      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26468      */
26469     minButtonWidth:75,
26470
26471     /**
26472      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26473      * This property cascades to child containers if not set.
26474      */
26475     labelAlign:'left',
26476
26477     /**
26478      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26479      * fires a looping event with that state. This is required to bind buttons to the valid
26480      * state using the config value formBind:true on the button.
26481      */
26482     monitorValid : false,
26483
26484     /**
26485      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26486      */
26487     monitorPoll : 200,
26488     
26489     /**
26490      * @cfg {String} progressUrl - Url to return progress data 
26491      */
26492     
26493     progressUrl : false,
26494   
26495     /**
26496      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26497      * fields are added and the column is closed. If no fields are passed the column remains open
26498      * until end() is called.
26499      * @param {Object} config The config to pass to the column
26500      * @param {Field} field1 (optional)
26501      * @param {Field} field2 (optional)
26502      * @param {Field} etc (optional)
26503      * @return Column The column container object
26504      */
26505     column : function(c){
26506         var col = new Roo.form.Column(c);
26507         this.start(col);
26508         if(arguments.length > 1){ // duplicate code required because of Opera
26509             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26510             this.end();
26511         }
26512         return col;
26513     },
26514
26515     /**
26516      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26517      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26518      * until end() is called.
26519      * @param {Object} config The config to pass to the fieldset
26520      * @param {Field} field1 (optional)
26521      * @param {Field} field2 (optional)
26522      * @param {Field} etc (optional)
26523      * @return FieldSet The fieldset container object
26524      */
26525     fieldset : function(c){
26526         var fs = new Roo.form.FieldSet(c);
26527         this.start(fs);
26528         if(arguments.length > 1){ // duplicate code required because of Opera
26529             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26530             this.end();
26531         }
26532         return fs;
26533     },
26534
26535     /**
26536      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26537      * fields are added and the container is closed. If no fields are passed the container remains open
26538      * until end() is called.
26539      * @param {Object} config The config to pass to the Layout
26540      * @param {Field} field1 (optional)
26541      * @param {Field} field2 (optional)
26542      * @param {Field} etc (optional)
26543      * @return Layout The container object
26544      */
26545     container : function(c){
26546         var l = new Roo.form.Layout(c);
26547         this.start(l);
26548         if(arguments.length > 1){ // duplicate code required because of Opera
26549             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26550             this.end();
26551         }
26552         return l;
26553     },
26554
26555     /**
26556      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26557      * @param {Object} container A Roo.form.Layout or subclass of Layout
26558      * @return {Form} this
26559      */
26560     start : function(c){
26561         // cascade label info
26562         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26563         this.active.stack.push(c);
26564         c.ownerCt = this.active;
26565         this.active = c;
26566         return this;
26567     },
26568
26569     /**
26570      * Closes the current open container
26571      * @return {Form} this
26572      */
26573     end : function(){
26574         if(this.active == this.root){
26575             return this;
26576         }
26577         this.active = this.active.ownerCt;
26578         return this;
26579     },
26580
26581     /**
26582      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26583      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26584      * as the label of the field.
26585      * @param {Field} field1
26586      * @param {Field} field2 (optional)
26587      * @param {Field} etc. (optional)
26588      * @return {Form} this
26589      */
26590     add : function(){
26591         this.active.stack.push.apply(this.active.stack, arguments);
26592         this.allItems.push.apply(this.allItems,arguments);
26593         var r = [];
26594         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26595             if(a[i].isFormField){
26596                 r.push(a[i]);
26597             }
26598         }
26599         if(r.length > 0){
26600             Roo.form.Form.superclass.add.apply(this, r);
26601         }
26602         return this;
26603     },
26604     
26605
26606     
26607     
26608     
26609      /**
26610      * Find any element that has been added to a form, using it's ID or name
26611      * This can include framesets, columns etc. along with regular fields..
26612      * @param {String} id - id or name to find.
26613      
26614      * @return {Element} e - or false if nothing found.
26615      */
26616     findbyId : function(id)
26617     {
26618         var ret = false;
26619         if (!id) {
26620             return ret;
26621         }
26622         Ext.each(this.allItems, function(f){
26623             if (f.id == id || f.name == id ){
26624                 ret = f;
26625                 return false;
26626             }
26627         });
26628         return ret;
26629     },
26630
26631     
26632     
26633     /**
26634      * Render this form into the passed container. This should only be called once!
26635      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26636      * @return {Form} this
26637      */
26638     render : function(ct)
26639     {
26640         
26641         
26642         
26643         ct = Roo.get(ct);
26644         var o = this.autoCreate || {
26645             tag: 'form',
26646             method : this.method || 'POST',
26647             id : this.id || Roo.id()
26648         };
26649         this.initEl(ct.createChild(o));
26650
26651         this.root.render(this.el);
26652         
26653        
26654              
26655         this.items.each(function(f){
26656             f.render('x-form-el-'+f.id);
26657         });
26658
26659         if(this.buttons.length > 0){
26660             // tables are required to maintain order and for correct IE layout
26661             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26662                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26663                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26664             }}, null, true);
26665             var tr = tb.getElementsByTagName('tr')[0];
26666             for(var i = 0, len = this.buttons.length; i < len; i++) {
26667                 var b = this.buttons[i];
26668                 var td = document.createElement('td');
26669                 td.className = 'x-form-btn-td';
26670                 b.render(tr.appendChild(td));
26671             }
26672         }
26673         if(this.monitorValid){ // initialize after render
26674             this.startMonitoring();
26675         }
26676         this.fireEvent('rendered', this);
26677         return this;
26678     },
26679
26680     /**
26681      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26682      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26683      * object or a valid Roo.DomHelper element config
26684      * @param {Function} handler The function called when the button is clicked
26685      * @param {Object} scope (optional) The scope of the handler function
26686      * @return {Roo.Button}
26687      */
26688     addButton : function(config, handler, scope){
26689         var bc = {
26690             handler: handler,
26691             scope: scope,
26692             minWidth: this.minButtonWidth,
26693             hideParent:true
26694         };
26695         if(typeof config == "string"){
26696             bc.text = config;
26697         }else{
26698             Roo.apply(bc, config);
26699         }
26700         var btn = new Roo.Button(null, bc);
26701         this.buttons.push(btn);
26702         return btn;
26703     },
26704
26705      /**
26706      * Adds a series of form elements (using the xtype property as the factory method.
26707      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26708      * @param {Object} config 
26709      */
26710     
26711     addxtype : function()
26712     {
26713         var ar = Array.prototype.slice.call(arguments, 0);
26714         var ret = false;
26715         for(var i = 0; i < ar.length; i++) {
26716             if (!ar[i]) {
26717                 continue; // skip -- if this happends something invalid got sent, we 
26718                 // should ignore it, as basically that interface element will not show up
26719                 // and that should be pretty obvious!!
26720             }
26721             
26722             if (Roo.form[ar[i].xtype]) {
26723                 ar[i].form = this;
26724                 var fe = Roo.factory(ar[i], Roo.form);
26725                 if (!ret) {
26726                     ret = fe;
26727                 }
26728                 fe.form = this;
26729                 if (fe.store) {
26730                     fe.store.form = this;
26731                 }
26732                 if (fe.isLayout) {  
26733                          
26734                     this.start(fe);
26735                     this.allItems.push(fe);
26736                     if (fe.items && fe.addxtype) {
26737                         fe.addxtype.apply(fe, fe.items);
26738                         delete fe.items;
26739                     }
26740                      this.end();
26741                     continue;
26742                 }
26743                 
26744                 
26745                  
26746                 this.add(fe);
26747               //  console.log('adding ' + ar[i].xtype);
26748             }
26749             if (ar[i].xtype == 'Button') {  
26750                 //console.log('adding button');
26751                 //console.log(ar[i]);
26752                 this.addButton(ar[i]);
26753                 this.allItems.push(fe);
26754                 continue;
26755             }
26756             
26757             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26758                 alert('end is not supported on xtype any more, use items');
26759             //    this.end();
26760             //    //console.log('adding end');
26761             }
26762             
26763         }
26764         return ret;
26765     },
26766     
26767     /**
26768      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26769      * option "monitorValid"
26770      */
26771     startMonitoring : function(){
26772         if(!this.bound){
26773             this.bound = true;
26774             Roo.TaskMgr.start({
26775                 run : this.bindHandler,
26776                 interval : this.monitorPoll || 200,
26777                 scope: this
26778             });
26779         }
26780     },
26781
26782     /**
26783      * Stops monitoring of the valid state of this form
26784      */
26785     stopMonitoring : function(){
26786         this.bound = false;
26787     },
26788
26789     // private
26790     bindHandler : function(){
26791         if(!this.bound){
26792             return false; // stops binding
26793         }
26794         var valid = true;
26795         this.items.each(function(f){
26796             if(!f.isValid(true)){
26797                 valid = false;
26798                 return false;
26799             }
26800         });
26801         for(var i = 0, len = this.buttons.length; i < len; i++){
26802             var btn = this.buttons[i];
26803             if(btn.formBind === true && btn.disabled === valid){
26804                 btn.setDisabled(!valid);
26805             }
26806         }
26807         this.fireEvent('clientvalidation', this, valid);
26808     }
26809     
26810     
26811     
26812     
26813     
26814     
26815     
26816     
26817 });
26818
26819
26820 // back compat
26821 Roo.Form = Roo.form.Form;
26822 /*
26823  * Based on:
26824  * Ext JS Library 1.1.1
26825  * Copyright(c) 2006-2007, Ext JS, LLC.
26826  *
26827  * Originally Released Under LGPL - original licence link has changed is not relivant.
26828  *
26829  * Fork - LGPL
26830  * <script type="text/javascript">
26831  */
26832  
26833  /**
26834  * @class Roo.form.Action
26835  * Internal Class used to handle form actions
26836  * @constructor
26837  * @param {Roo.form.BasicForm} el The form element or its id
26838  * @param {Object} config Configuration options
26839  */
26840  
26841  
26842 // define the action interface
26843 Roo.form.Action = function(form, options){
26844     this.form = form;
26845     this.options = options || {};
26846 };
26847 /**
26848  * Client Validation Failed
26849  * @const 
26850  */
26851 Roo.form.Action.CLIENT_INVALID = 'client';
26852 /**
26853  * Server Validation Failed
26854  * @const 
26855  */
26856  Roo.form.Action.SERVER_INVALID = 'server';
26857  /**
26858  * Connect to Server Failed
26859  * @const 
26860  */
26861 Roo.form.Action.CONNECT_FAILURE = 'connect';
26862 /**
26863  * Reading Data from Server Failed
26864  * @const 
26865  */
26866 Roo.form.Action.LOAD_FAILURE = 'load';
26867
26868 Roo.form.Action.prototype = {
26869     type : 'default',
26870     failureType : undefined,
26871     response : undefined,
26872     result : undefined,
26873
26874     // interface method
26875     run : function(options){
26876
26877     },
26878
26879     // interface method
26880     success : function(response){
26881
26882     },
26883
26884     // interface method
26885     handleResponse : function(response){
26886
26887     },
26888
26889     // default connection failure
26890     failure : function(response){
26891         this.response = response;
26892         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26893         this.form.afterAction(this, false);
26894     },
26895
26896     processResponse : function(response){
26897         this.response = response;
26898         if(!response.responseText){
26899             return true;
26900         }
26901         this.result = this.handleResponse(response);
26902         return this.result;
26903     },
26904
26905     // utility functions used internally
26906     getUrl : function(appendParams){
26907         var url = this.options.url || this.form.url || this.form.el.dom.action;
26908         if(appendParams){
26909             var p = this.getParams();
26910             if(p){
26911                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26912             }
26913         }
26914         return url;
26915     },
26916
26917     getMethod : function(){
26918         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26919     },
26920
26921     getParams : function(){
26922         var bp = this.form.baseParams;
26923         var p = this.options.params;
26924         if(p){
26925             if(typeof p == "object"){
26926                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26927             }else if(typeof p == 'string' && bp){
26928                 p += '&' + Roo.urlEncode(bp);
26929             }
26930         }else if(bp){
26931             p = Roo.urlEncode(bp);
26932         }
26933         return p;
26934     },
26935
26936     createCallback : function(){
26937         return {
26938             success: this.success,
26939             failure: this.failure,
26940             scope: this,
26941             timeout: (this.form.timeout*1000),
26942             upload: this.form.fileUpload ? this.success : undefined
26943         };
26944     }
26945 };
26946
26947 Roo.form.Action.Submit = function(form, options){
26948     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26949 };
26950
26951 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26952     type : 'submit',
26953
26954     haveProgress : false,
26955     uploadComplete : false,
26956     
26957     // uploadProgress indicator.
26958     uploadProgress : function()
26959     {
26960         if (!this.form.progressUrl) {
26961             return;
26962         }
26963         
26964         if (!this.haveProgress) {
26965             Roo.MessageBox.progress("Uploading", "Uploading");
26966         }
26967         if (this.uploadComplete) {
26968            Roo.MessageBox.hide();
26969            return;
26970         }
26971         
26972         this.haveProgress = true;
26973    
26974         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26975         
26976         var c = new Roo.data.Connection();
26977         c.request({
26978             url : this.form.progressUrl,
26979             params: {
26980                 id : uid
26981             },
26982             method: 'GET',
26983             success : function(req){
26984                //console.log(data);
26985                 var rdata = false;
26986                 var edata;
26987                 try  {
26988                    rdata = Roo.decode(req.responseText)
26989                 } catch (e) {
26990                     Roo.log("Invalid data from server..");
26991                     Roo.log(edata);
26992                     return;
26993                 }
26994                 if (!rdata || !rdata.success) {
26995                     Roo.log(rdata);
26996                     return;
26997                 }
26998                 var data = rdata.data;
26999                 
27000                 if (this.uploadComplete) {
27001                    Roo.MessageBox.hide();
27002                    return;
27003                 }
27004                    
27005                 if (data){
27006                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27007                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27008                     );
27009                 }
27010                 this.uploadProgress.defer(2000,this);
27011             },
27012        
27013             failure: function(data) {
27014                 Roo.log('progress url failed ');
27015                 Roo.log(data);
27016             },
27017             scope : this
27018         });
27019            
27020     },
27021     
27022     
27023     run : function()
27024     {
27025         // run get Values on the form, so it syncs any secondary forms.
27026         this.form.getValues();
27027         
27028         var o = this.options;
27029         var method = this.getMethod();
27030         var isPost = method == 'POST';
27031         if(o.clientValidation === false || this.form.isValid()){
27032             
27033             if (this.form.progressUrl) {
27034                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27035                     (new Date() * 1) + '' + Math.random());
27036                     
27037             } 
27038             
27039             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27040                 form:this.form.el.dom,
27041                 url:this.getUrl(!isPost),
27042                 method: method,
27043                 params:isPost ? this.getParams() : null,
27044                 isUpload: this.form.fileUpload
27045             }));
27046             
27047             this.uploadProgress();
27048
27049         }else if (o.clientValidation !== false){ // client validation failed
27050             this.failureType = Roo.form.Action.CLIENT_INVALID;
27051             this.form.afterAction(this, false);
27052         }
27053     },
27054
27055     success : function(response)
27056     {
27057         this.uploadComplete= true;
27058         if (this.haveProgress) {
27059             Roo.MessageBox.hide();
27060         }
27061         
27062         var result = this.processResponse(response);
27063         if(result === true || result.success){
27064             this.form.afterAction(this, true);
27065             return;
27066         }
27067         if(result.errors){
27068             this.form.markInvalid(result.errors);
27069             this.failureType = Roo.form.Action.SERVER_INVALID;
27070         }
27071         this.form.afterAction(this, false);
27072     },
27073     failure : function(response)
27074     {
27075         this.uploadComplete= true;
27076         if (this.haveProgress) {
27077             Roo.MessageBox.hide();
27078         }
27079         
27080         this.response = response;
27081         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27082         this.form.afterAction(this, false);
27083     },
27084     
27085     handleResponse : function(response){
27086         if(this.form.errorReader){
27087             var rs = this.form.errorReader.read(response);
27088             var errors = [];
27089             if(rs.records){
27090                 for(var i = 0, len = rs.records.length; i < len; i++) {
27091                     var r = rs.records[i];
27092                     errors[i] = r.data;
27093                 }
27094             }
27095             if(errors.length < 1){
27096                 errors = null;
27097             }
27098             return {
27099                 success : rs.success,
27100                 errors : errors
27101             };
27102         }
27103         var ret = false;
27104         try {
27105             ret = Roo.decode(response.responseText);
27106         } catch (e) {
27107             ret = {
27108                 success: false,
27109                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27110                 errors : []
27111             };
27112         }
27113         return ret;
27114         
27115     }
27116 });
27117
27118
27119 Roo.form.Action.Load = function(form, options){
27120     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27121     this.reader = this.form.reader;
27122 };
27123
27124 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27125     type : 'load',
27126
27127     run : function(){
27128         Roo.Ajax.request(Roo.apply(
27129                 this.createCallback(), {
27130                     method:this.getMethod(),
27131                     url:this.getUrl(false),
27132                     params:this.getParams()
27133         }));
27134     },
27135
27136     success : function(response){
27137         var result = this.processResponse(response);
27138         if(result === true || !result.success || !result.data){
27139             this.failureType = Roo.form.Action.LOAD_FAILURE;
27140             this.form.afterAction(this, false);
27141             return;
27142         }
27143         this.form.clearInvalid();
27144         this.form.setValues(result.data);
27145         this.form.afterAction(this, true);
27146     },
27147
27148     handleResponse : function(response){
27149         if(this.form.reader){
27150             var rs = this.form.reader.read(response);
27151             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27152             return {
27153                 success : rs.success,
27154                 data : data
27155             };
27156         }
27157         return Roo.decode(response.responseText);
27158     }
27159 });
27160
27161 Roo.form.Action.ACTION_TYPES = {
27162     'load' : Roo.form.Action.Load,
27163     'submit' : Roo.form.Action.Submit
27164 };/*
27165  * Based on:
27166  * Ext JS Library 1.1.1
27167  * Copyright(c) 2006-2007, Ext JS, LLC.
27168  *
27169  * Originally Released Under LGPL - original licence link has changed is not relivant.
27170  *
27171  * Fork - LGPL
27172  * <script type="text/javascript">
27173  */
27174  
27175 /**
27176  * @class Roo.form.Layout
27177  * @extends Roo.Component
27178  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27179  * @constructor
27180  * @param {Object} config Configuration options
27181  */
27182 Roo.form.Layout = function(config){
27183     var xitems = [];
27184     if (config.items) {
27185         xitems = config.items;
27186         delete config.items;
27187     }
27188     Roo.form.Layout.superclass.constructor.call(this, config);
27189     this.stack = [];
27190     Roo.each(xitems, this.addxtype, this);
27191      
27192 };
27193
27194 Roo.extend(Roo.form.Layout, Roo.Component, {
27195     /**
27196      * @cfg {String/Object} autoCreate
27197      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27198      */
27199     /**
27200      * @cfg {String/Object/Function} style
27201      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27202      * a function which returns such a specification.
27203      */
27204     /**
27205      * @cfg {String} labelAlign
27206      * Valid values are "left," "top" and "right" (defaults to "left")
27207      */
27208     /**
27209      * @cfg {Number} labelWidth
27210      * Fixed width in pixels of all field labels (defaults to undefined)
27211      */
27212     /**
27213      * @cfg {Boolean} clear
27214      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27215      */
27216     clear : true,
27217     /**
27218      * @cfg {String} labelSeparator
27219      * The separator to use after field labels (defaults to ':')
27220      */
27221     labelSeparator : ':',
27222     /**
27223      * @cfg {Boolean} hideLabels
27224      * True to suppress the display of field labels in this layout (defaults to false)
27225      */
27226     hideLabels : false,
27227
27228     // private
27229     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27230     
27231     isLayout : true,
27232     
27233     // private
27234     onRender : function(ct, position){
27235         if(this.el){ // from markup
27236             this.el = Roo.get(this.el);
27237         }else {  // generate
27238             var cfg = this.getAutoCreate();
27239             this.el = ct.createChild(cfg, position);
27240         }
27241         if(this.style){
27242             this.el.applyStyles(this.style);
27243         }
27244         if(this.labelAlign){
27245             this.el.addClass('x-form-label-'+this.labelAlign);
27246         }
27247         if(this.hideLabels){
27248             this.labelStyle = "display:none";
27249             this.elementStyle = "padding-left:0;";
27250         }else{
27251             if(typeof this.labelWidth == 'number'){
27252                 this.labelStyle = "width:"+this.labelWidth+"px;";
27253                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27254             }
27255             if(this.labelAlign == 'top'){
27256                 this.labelStyle = "width:auto;";
27257                 this.elementStyle = "padding-left:0;";
27258             }
27259         }
27260         var stack = this.stack;
27261         var slen = stack.length;
27262         if(slen > 0){
27263             if(!this.fieldTpl){
27264                 var t = new Roo.Template(
27265                     '<div class="x-form-item {5}">',
27266                         '<label for="{0}" style="{2}">{1}{4}</label>',
27267                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27268                         '</div>',
27269                     '</div><div class="x-form-clear-left"></div>'
27270                 );
27271                 t.disableFormats = true;
27272                 t.compile();
27273                 Roo.form.Layout.prototype.fieldTpl = t;
27274             }
27275             for(var i = 0; i < slen; i++) {
27276                 if(stack[i].isFormField){
27277                     this.renderField(stack[i]);
27278                 }else{
27279                     this.renderComponent(stack[i]);
27280                 }
27281             }
27282         }
27283         if(this.clear){
27284             this.el.createChild({cls:'x-form-clear'});
27285         }
27286     },
27287
27288     // private
27289     renderField : function(f){
27290         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27291                f.id, //0
27292                f.fieldLabel, //1
27293                f.labelStyle||this.labelStyle||'', //2
27294                this.elementStyle||'', //3
27295                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27296                f.itemCls||this.itemCls||''  //5
27297        ], true).getPrevSibling());
27298     },
27299
27300     // private
27301     renderComponent : function(c){
27302         c.render(c.isLayout ? this.el : this.el.createChild());    
27303     },
27304     /**
27305      * Adds a object form elements (using the xtype property as the factory method.)
27306      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27307      * @param {Object} config 
27308      */
27309     addxtype : function(o)
27310     {
27311         // create the lement.
27312         o.form = this.form;
27313         var fe = Roo.factory(o, Roo.form);
27314         this.form.allItems.push(fe);
27315         this.stack.push(fe);
27316         
27317         if (fe.isFormField) {
27318             this.form.items.add(fe);
27319         }
27320          
27321         return fe;
27322     }
27323 });
27324
27325 /**
27326  * @class Roo.form.Column
27327  * @extends Roo.form.Layout
27328  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27329  * @constructor
27330  * @param {Object} config Configuration options
27331  */
27332 Roo.form.Column = function(config){
27333     Roo.form.Column.superclass.constructor.call(this, config);
27334 };
27335
27336 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27337     /**
27338      * @cfg {Number/String} width
27339      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27340      */
27341     /**
27342      * @cfg {String/Object} autoCreate
27343      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27344      */
27345
27346     // private
27347     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27348
27349     // private
27350     onRender : function(ct, position){
27351         Roo.form.Column.superclass.onRender.call(this, ct, position);
27352         if(this.width){
27353             this.el.setWidth(this.width);
27354         }
27355     }
27356 });
27357
27358
27359 /**
27360  * @class Roo.form.Row
27361  * @extends Roo.form.Layout
27362  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27363  * @constructor
27364  * @param {Object} config Configuration options
27365  */
27366
27367  
27368 Roo.form.Row = function(config){
27369     Roo.form.Row.superclass.constructor.call(this, config);
27370 };
27371  
27372 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27373       /**
27374      * @cfg {Number/String} width
27375      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27376      */
27377     /**
27378      * @cfg {Number/String} height
27379      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27380      */
27381     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27382     
27383     padWidth : 20,
27384     // private
27385     onRender : function(ct, position){
27386         //console.log('row render');
27387         if(!this.rowTpl){
27388             var t = new Roo.Template(
27389                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27390                     '<label for="{0}" style="{2}">{1}{4}</label>',
27391                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27392                     '</div>',
27393                 '</div>'
27394             );
27395             t.disableFormats = true;
27396             t.compile();
27397             Roo.form.Layout.prototype.rowTpl = t;
27398         }
27399         this.fieldTpl = this.rowTpl;
27400         
27401         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27402         var labelWidth = 100;
27403         
27404         if ((this.labelAlign != 'top')) {
27405             if (typeof this.labelWidth == 'number') {
27406                 labelWidth = this.labelWidth
27407             }
27408             this.padWidth =  20 + labelWidth;
27409             
27410         }
27411         
27412         Roo.form.Column.superclass.onRender.call(this, ct, position);
27413         if(this.width){
27414             this.el.setWidth(this.width);
27415         }
27416         if(this.height){
27417             this.el.setHeight(this.height);
27418         }
27419     },
27420     
27421     // private
27422     renderField : function(f){
27423         f.fieldEl = this.fieldTpl.append(this.el, [
27424                f.id, f.fieldLabel,
27425                f.labelStyle||this.labelStyle||'',
27426                this.elementStyle||'',
27427                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27428                f.itemCls||this.itemCls||'',
27429                f.width ? f.width + this.padWidth : 160 + this.padWidth
27430        ],true);
27431     }
27432 });
27433  
27434
27435 /**
27436  * @class Roo.form.FieldSet
27437  * @extends Roo.form.Layout
27438  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27439  * @constructor
27440  * @param {Object} config Configuration options
27441  */
27442 Roo.form.FieldSet = function(config){
27443     Roo.form.FieldSet.superclass.constructor.call(this, config);
27444 };
27445
27446 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27447     /**
27448      * @cfg {String} legend
27449      * The text to display as the legend for the FieldSet (defaults to '')
27450      */
27451     /**
27452      * @cfg {String/Object} autoCreate
27453      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27454      */
27455
27456     // private
27457     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27458
27459     // private
27460     onRender : function(ct, position){
27461         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27462         if(this.legend){
27463             this.setLegend(this.legend);
27464         }
27465     },
27466
27467     // private
27468     setLegend : function(text){
27469         if(this.rendered){
27470             this.el.child('legend').update(text);
27471         }
27472     }
27473 });/*
27474  * Based on:
27475  * Ext JS Library 1.1.1
27476  * Copyright(c) 2006-2007, Ext JS, LLC.
27477  *
27478  * Originally Released Under LGPL - original licence link has changed is not relivant.
27479  *
27480  * Fork - LGPL
27481  * <script type="text/javascript">
27482  */
27483 /**
27484  * @class Roo.form.VTypes
27485  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27486  * @singleton
27487  */
27488 Roo.form.VTypes = function(){
27489     // closure these in so they are only created once.
27490     var alpha = /^[a-zA-Z_]+$/;
27491     var alphanum = /^[a-zA-Z0-9_]+$/;
27492     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27493     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27494
27495     // All these messages and functions are configurable
27496     return {
27497         /**
27498          * The function used to validate email addresses
27499          * @param {String} value The email address
27500          */
27501         'email' : function(v){
27502             return email.test(v);
27503         },
27504         /**
27505          * The error text to display when the email validation function returns false
27506          * @type String
27507          */
27508         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27509         /**
27510          * The keystroke filter mask to be applied on email input
27511          * @type RegExp
27512          */
27513         'emailMask' : /[a-z0-9_\.\-@]/i,
27514
27515         /**
27516          * The function used to validate URLs
27517          * @param {String} value The URL
27518          */
27519         'url' : function(v){
27520             return url.test(v);
27521         },
27522         /**
27523          * The error text to display when the url validation function returns false
27524          * @type String
27525          */
27526         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27527         
27528         /**
27529          * The function used to validate alpha values
27530          * @param {String} value The value
27531          */
27532         'alpha' : function(v){
27533             return alpha.test(v);
27534         },
27535         /**
27536          * The error text to display when the alpha validation function returns false
27537          * @type String
27538          */
27539         'alphaText' : 'This field should only contain letters and _',
27540         /**
27541          * The keystroke filter mask to be applied on alpha input
27542          * @type RegExp
27543          */
27544         'alphaMask' : /[a-z_]/i,
27545
27546         /**
27547          * The function used to validate alphanumeric values
27548          * @param {String} value The value
27549          */
27550         'alphanum' : function(v){
27551             return alphanum.test(v);
27552         },
27553         /**
27554          * The error text to display when the alphanumeric validation function returns false
27555          * @type String
27556          */
27557         'alphanumText' : 'This field should only contain letters, numbers and _',
27558         /**
27559          * The keystroke filter mask to be applied on alphanumeric input
27560          * @type RegExp
27561          */
27562         'alphanumMask' : /[a-z0-9_]/i
27563     };
27564 }();//<script type="text/javascript">
27565
27566 /**
27567  * @class Roo.form.FCKeditor
27568  * @extends Roo.form.TextArea
27569  * Wrapper around the FCKEditor http://www.fckeditor.net
27570  * @constructor
27571  * Creates a new FCKeditor
27572  * @param {Object} config Configuration options
27573  */
27574 Roo.form.FCKeditor = function(config){
27575     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27576     this.addEvents({
27577          /**
27578          * @event editorinit
27579          * Fired when the editor is initialized - you can add extra handlers here..
27580          * @param {FCKeditor} this
27581          * @param {Object} the FCK object.
27582          */
27583         editorinit : true
27584     });
27585     
27586     
27587 };
27588 Roo.form.FCKeditor.editors = { };
27589 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27590 {
27591     //defaultAutoCreate : {
27592     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27593     //},
27594     // private
27595     /**
27596      * @cfg {Object} fck options - see fck manual for details.
27597      */
27598     fckconfig : false,
27599     
27600     /**
27601      * @cfg {Object} fck toolbar set (Basic or Default)
27602      */
27603     toolbarSet : 'Basic',
27604     /**
27605      * @cfg {Object} fck BasePath
27606      */ 
27607     basePath : '/fckeditor/',
27608     
27609     
27610     frame : false,
27611     
27612     value : '',
27613     
27614    
27615     onRender : function(ct, position)
27616     {
27617         if(!this.el){
27618             this.defaultAutoCreate = {
27619                 tag: "textarea",
27620                 style:"width:300px;height:60px;",
27621                 autocomplete: "off"
27622             };
27623         }
27624         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27625         /*
27626         if(this.grow){
27627             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27628             if(this.preventScrollbars){
27629                 this.el.setStyle("overflow", "hidden");
27630             }
27631             this.el.setHeight(this.growMin);
27632         }
27633         */
27634         //console.log('onrender' + this.getId() );
27635         Roo.form.FCKeditor.editors[this.getId()] = this;
27636          
27637
27638         this.replaceTextarea() ;
27639         
27640     },
27641     
27642     getEditor : function() {
27643         return this.fckEditor;
27644     },
27645     /**
27646      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27647      * @param {Mixed} value The value to set
27648      */
27649     
27650     
27651     setValue : function(value)
27652     {
27653         //console.log('setValue: ' + value);
27654         
27655         if(typeof(value) == 'undefined') { // not sure why this is happending...
27656             return;
27657         }
27658         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27659         
27660         //if(!this.el || !this.getEditor()) {
27661         //    this.value = value;
27662             //this.setValue.defer(100,this,[value]);    
27663         //    return;
27664         //} 
27665         
27666         if(!this.getEditor()) {
27667             return;
27668         }
27669         
27670         this.getEditor().SetData(value);
27671         
27672         //
27673
27674     },
27675
27676     /**
27677      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27678      * @return {Mixed} value The field value
27679      */
27680     getValue : function()
27681     {
27682         
27683         if (this.frame && this.frame.dom.style.display == 'none') {
27684             return Roo.form.FCKeditor.superclass.getValue.call(this);
27685         }
27686         
27687         if(!this.el || !this.getEditor()) {
27688            
27689            // this.getValue.defer(100,this); 
27690             return this.value;
27691         }
27692        
27693         
27694         var value=this.getEditor().GetData();
27695         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27696         return Roo.form.FCKeditor.superclass.getValue.call(this);
27697         
27698
27699     },
27700
27701     /**
27702      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27703      * @return {Mixed} value The field value
27704      */
27705     getRawValue : function()
27706     {
27707         if (this.frame && this.frame.dom.style.display == 'none') {
27708             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27709         }
27710         
27711         if(!this.el || !this.getEditor()) {
27712             //this.getRawValue.defer(100,this); 
27713             return this.value;
27714             return;
27715         }
27716         
27717         
27718         
27719         var value=this.getEditor().GetData();
27720         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27721         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27722          
27723     },
27724     
27725     setSize : function(w,h) {
27726         
27727         
27728         
27729         //if (this.frame && this.frame.dom.style.display == 'none') {
27730         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27731         //    return;
27732         //}
27733         //if(!this.el || !this.getEditor()) {
27734         //    this.setSize.defer(100,this, [w,h]); 
27735         //    return;
27736         //}
27737         
27738         
27739         
27740         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27741         
27742         this.frame.dom.setAttribute('width', w);
27743         this.frame.dom.setAttribute('height', h);
27744         this.frame.setSize(w,h);
27745         
27746     },
27747     
27748     toggleSourceEdit : function(value) {
27749         
27750       
27751          
27752         this.el.dom.style.display = value ? '' : 'none';
27753         this.frame.dom.style.display = value ?  'none' : '';
27754         
27755     },
27756     
27757     
27758     focus: function(tag)
27759     {
27760         if (this.frame.dom.style.display == 'none') {
27761             return Roo.form.FCKeditor.superclass.focus.call(this);
27762         }
27763         if(!this.el || !this.getEditor()) {
27764             this.focus.defer(100,this, [tag]); 
27765             return;
27766         }
27767         
27768         
27769         
27770         
27771         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27772         this.getEditor().Focus();
27773         if (tgs.length) {
27774             if (!this.getEditor().Selection.GetSelection()) {
27775                 this.focus.defer(100,this, [tag]); 
27776                 return;
27777             }
27778             
27779             
27780             var r = this.getEditor().EditorDocument.createRange();
27781             r.setStart(tgs[0],0);
27782             r.setEnd(tgs[0],0);
27783             this.getEditor().Selection.GetSelection().removeAllRanges();
27784             this.getEditor().Selection.GetSelection().addRange(r);
27785             this.getEditor().Focus();
27786         }
27787         
27788     },
27789     
27790     
27791     
27792     replaceTextarea : function()
27793     {
27794         if ( document.getElementById( this.getId() + '___Frame' ) )
27795             return ;
27796         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27797         //{
27798             // We must check the elements firstly using the Id and then the name.
27799         var oTextarea = document.getElementById( this.getId() );
27800         
27801         var colElementsByName = document.getElementsByName( this.getId() ) ;
27802          
27803         oTextarea.style.display = 'none' ;
27804
27805         if ( oTextarea.tabIndex ) {            
27806             this.TabIndex = oTextarea.tabIndex ;
27807         }
27808         
27809         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27810         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27811         this.frame = Roo.get(this.getId() + '___Frame')
27812     },
27813     
27814     _getConfigHtml : function()
27815     {
27816         var sConfig = '' ;
27817
27818         for ( var o in this.fckconfig ) {
27819             sConfig += sConfig.length > 0  ? '&amp;' : '';
27820             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27821         }
27822
27823         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27824     },
27825     
27826     
27827     _getIFrameHtml : function()
27828     {
27829         var sFile = 'fckeditor.html' ;
27830         /* no idea what this is about..
27831         try
27832         {
27833             if ( (/fcksource=true/i).test( window.top.location.search ) )
27834                 sFile = 'fckeditor.original.html' ;
27835         }
27836         catch (e) { 
27837         */
27838
27839         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27840         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27841         
27842         
27843         var html = '<iframe id="' + this.getId() +
27844             '___Frame" src="' + sLink +
27845             '" width="' + this.width +
27846             '" height="' + this.height + '"' +
27847             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27848             ' frameborder="0" scrolling="no"></iframe>' ;
27849
27850         return html ;
27851     },
27852     
27853     _insertHtmlBefore : function( html, element )
27854     {
27855         if ( element.insertAdjacentHTML )       {
27856             // IE
27857             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27858         } else { // Gecko
27859             var oRange = document.createRange() ;
27860             oRange.setStartBefore( element ) ;
27861             var oFragment = oRange.createContextualFragment( html );
27862             element.parentNode.insertBefore( oFragment, element ) ;
27863         }
27864     }
27865     
27866     
27867   
27868     
27869     
27870     
27871     
27872
27873 });
27874
27875 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27876
27877 function FCKeditor_OnComplete(editorInstance){
27878     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27879     f.fckEditor = editorInstance;
27880     //console.log("loaded");
27881     f.fireEvent('editorinit', f, editorInstance);
27882
27883   
27884
27885  
27886
27887
27888
27889
27890
27891
27892
27893
27894
27895
27896
27897
27898
27899
27900
27901 //<script type="text/javascript">
27902 /**
27903  * @class Roo.form.GridField
27904  * @extends Roo.form.Field
27905  * Embed a grid (or editable grid into a form)
27906  * STATUS ALPHA
27907  * 
27908  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27909  * it needs 
27910  * xgrid.store = Roo.data.Store
27911  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27912  * xgrid.store.reader = Roo.data.JsonReader 
27913  * 
27914  * 
27915  * @constructor
27916  * Creates a new GridField
27917  * @param {Object} config Configuration options
27918  */
27919 Roo.form.GridField = function(config){
27920     Roo.form.GridField.superclass.constructor.call(this, config);
27921      
27922 };
27923
27924 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27925     /**
27926      * @cfg {Number} width  - used to restrict width of grid..
27927      */
27928     width : 100,
27929     /**
27930      * @cfg {Number} height - used to restrict height of grid..
27931      */
27932     height : 50,
27933      /**
27934      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27935          * 
27936          *}
27937      */
27938     xgrid : false, 
27939     /**
27940      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27941      * {tag: "input", type: "checkbox", autocomplete: "off"})
27942      */
27943    // defaultAutoCreate : { tag: 'div' },
27944     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27945     /**
27946      * @cfg {String} addTitle Text to include for adding a title.
27947      */
27948     addTitle : false,
27949     //
27950     onResize : function(){
27951         Roo.form.Field.superclass.onResize.apply(this, arguments);
27952     },
27953
27954     initEvents : function(){
27955         // Roo.form.Checkbox.superclass.initEvents.call(this);
27956         // has no events...
27957        
27958     },
27959
27960
27961     getResizeEl : function(){
27962         return this.wrap;
27963     },
27964
27965     getPositionEl : function(){
27966         return this.wrap;
27967     },
27968
27969     // private
27970     onRender : function(ct, position){
27971         
27972         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27973         var style = this.style;
27974         delete this.style;
27975         
27976         Roo.form.GridField.superclass.onRender.call(this, ct, position);
27977         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27978         this.viewEl = this.wrap.createChild({ tag: 'div' });
27979         if (style) {
27980             this.viewEl.applyStyles(style);
27981         }
27982         if (this.width) {
27983             this.viewEl.setWidth(this.width);
27984         }
27985         if (this.height) {
27986             this.viewEl.setHeight(this.height);
27987         }
27988         //if(this.inputValue !== undefined){
27989         //this.setValue(this.value);
27990         
27991         
27992         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27993         
27994         
27995         this.grid.render();
27996         this.grid.getDataSource().on('remove', this.refreshValue, this);
27997         this.grid.getDataSource().on('update', this.refreshValue, this);
27998         this.grid.on('afteredit', this.refreshValue, this);
27999  
28000     },
28001      
28002     
28003     /**
28004      * Sets the value of the item. 
28005      * @param {String} either an object  or a string..
28006      */
28007     setValue : function(v){
28008         //this.value = v;
28009         v = v || []; // empty set..
28010         // this does not seem smart - it really only affects memoryproxy grids..
28011         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28012             var ds = this.grid.getDataSource();
28013             // assumes a json reader..
28014             var data = {}
28015             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28016             ds.loadData( data);
28017         }
28018         Roo.form.GridField.superclass.setValue.call(this, v);
28019         this.refreshValue();
28020         // should load data in the grid really....
28021     },
28022     
28023     // private
28024     refreshValue: function() {
28025          var val = [];
28026         this.grid.getDataSource().each(function(r) {
28027             val.push(r.data);
28028         });
28029         this.el.dom.value = Roo.encode(val);
28030     }
28031     
28032      
28033     
28034     
28035 });/*
28036  * Based on:
28037  * Ext JS Library 1.1.1
28038  * Copyright(c) 2006-2007, Ext JS, LLC.
28039  *
28040  * Originally Released Under LGPL - original licence link has changed is not relivant.
28041  *
28042  * Fork - LGPL
28043  * <script type="text/javascript">
28044  */
28045 /**
28046  * @class Roo.form.DisplayField
28047  * @extends Roo.form.Field
28048  * A generic Field to display non-editable data.
28049  * @constructor
28050  * Creates a new Display Field item.
28051  * @param {Object} config Configuration options
28052  */
28053 Roo.form.DisplayField = function(config){
28054     Roo.form.DisplayField.superclass.constructor.call(this, config);
28055     
28056 };
28057
28058 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28059     inputType:      'hidden',
28060     allowBlank:     true,
28061     readOnly:         true,
28062     
28063  
28064     /**
28065      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28066      */
28067     focusClass : undefined,
28068     /**
28069      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28070      */
28071     fieldClass: 'x-form-field',
28072     
28073      /**
28074      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28075      */
28076     valueRenderer: undefined,
28077     
28078     width: 100,
28079     /**
28080      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28081      * {tag: "input", type: "checkbox", autocomplete: "off"})
28082      */
28083      
28084  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28085
28086     onResize : function(){
28087         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28088         
28089     },
28090
28091     initEvents : function(){
28092         // Roo.form.Checkbox.superclass.initEvents.call(this);
28093         // has no events...
28094        
28095     },
28096
28097
28098     getResizeEl : function(){
28099         return this.wrap;
28100     },
28101
28102     getPositionEl : function(){
28103         return this.wrap;
28104     },
28105
28106     // private
28107     onRender : function(ct, position){
28108         
28109         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28110         //if(this.inputValue !== undefined){
28111         this.wrap = this.el.wrap();
28112         
28113         this.viewEl = this.wrap.createChild({ tag: 'div'});
28114         
28115         if (this.bodyStyle) {
28116             this.viewEl.applyStyles(this.bodyStyle);
28117         }
28118         //this.viewEl.setStyle('padding', '2px');
28119         
28120         this.setValue(this.value);
28121         
28122     },
28123 /*
28124     // private
28125     initValue : Roo.emptyFn,
28126
28127   */
28128
28129         // private
28130     onClick : function(){
28131         
28132     },
28133
28134     /**
28135      * Sets the checked state of the checkbox.
28136      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28137      */
28138     setValue : function(v){
28139         this.value = v;
28140         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28141         // this might be called before we have a dom element..
28142         if (!this.viewEl) {
28143             return;
28144         }
28145         this.viewEl.dom.innerHTML = html;
28146         Roo.form.DisplayField.superclass.setValue.call(this, v);
28147
28148     }
28149 });//<script type="text/javasscript">
28150  
28151
28152 /**
28153  * @class Roo.DDView
28154  * A DnD enabled version of Roo.View.
28155  * @param {Element/String} container The Element in which to create the View.
28156  * @param {String} tpl The template string used to create the markup for each element of the View
28157  * @param {Object} config The configuration properties. These include all the config options of
28158  * {@link Roo.View} plus some specific to this class.<br>
28159  * <p>
28160  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28161  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28162  * <p>
28163  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28164 .x-view-drag-insert-above {
28165         border-top:1px dotted #3366cc;
28166 }
28167 .x-view-drag-insert-below {
28168         border-bottom:1px dotted #3366cc;
28169 }
28170 </code></pre>
28171  * 
28172  */
28173  
28174 Roo.DDView = function(container, tpl, config) {
28175     Roo.DDView.superclass.constructor.apply(this, arguments);
28176     this.getEl().setStyle("outline", "0px none");
28177     this.getEl().unselectable();
28178     if (this.dragGroup) {
28179                 this.setDraggable(this.dragGroup.split(","));
28180     }
28181     if (this.dropGroup) {
28182                 this.setDroppable(this.dropGroup.split(","));
28183     }
28184     if (this.deletable) {
28185         this.setDeletable();
28186     }
28187     this.isDirtyFlag = false;
28188         this.addEvents({
28189                 "drop" : true
28190         });
28191 };
28192
28193 Roo.extend(Roo.DDView, Roo.View, {
28194 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28195 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28196 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28197 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28198
28199         isFormField: true,
28200
28201         reset: Roo.emptyFn,
28202         
28203         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28204
28205         validate: function() {
28206                 return true;
28207         },
28208         
28209         destroy: function() {
28210                 this.purgeListeners();
28211                 this.getEl.removeAllListeners();
28212                 this.getEl().remove();
28213                 if (this.dragZone) {
28214                         if (this.dragZone.destroy) {
28215                                 this.dragZone.destroy();
28216                         }
28217                 }
28218                 if (this.dropZone) {
28219                         if (this.dropZone.destroy) {
28220                                 this.dropZone.destroy();
28221                         }
28222                 }
28223         },
28224
28225 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28226         getName: function() {
28227                 return this.name;
28228         },
28229
28230 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28231         setValue: function(v) {
28232                 if (!this.store) {
28233                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28234                 }
28235                 var data = {};
28236                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28237                 this.store.proxy = new Roo.data.MemoryProxy(data);
28238                 this.store.load();
28239         },
28240
28241 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28242         getValue: function() {
28243                 var result = '(';
28244                 this.store.each(function(rec) {
28245                         result += rec.id + ',';
28246                 });
28247                 return result.substr(0, result.length - 1) + ')';
28248         },
28249         
28250         getIds: function() {
28251                 var i = 0, result = new Array(this.store.getCount());
28252                 this.store.each(function(rec) {
28253                         result[i++] = rec.id;
28254                 });
28255                 return result;
28256         },
28257         
28258         isDirty: function() {
28259                 return this.isDirtyFlag;
28260         },
28261
28262 /**
28263  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28264  *      whole Element becomes the target, and this causes the drop gesture to append.
28265  */
28266     getTargetFromEvent : function(e) {
28267                 var target = e.getTarget();
28268                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28269                 target = target.parentNode;
28270                 }
28271                 if (!target) {
28272                         target = this.el.dom.lastChild || this.el.dom;
28273                 }
28274                 return target;
28275     },
28276
28277 /**
28278  *      Create the drag data which consists of an object which has the property "ddel" as
28279  *      the drag proxy element. 
28280  */
28281     getDragData : function(e) {
28282         var target = this.findItemFromChild(e.getTarget());
28283                 if(target) {
28284                         this.handleSelection(e);
28285                         var selNodes = this.getSelectedNodes();
28286             var dragData = {
28287                 source: this,
28288                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28289                 nodes: selNodes,
28290                 records: []
28291                         };
28292                         var selectedIndices = this.getSelectedIndexes();
28293                         for (var i = 0; i < selectedIndices.length; i++) {
28294                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28295                         }
28296                         if (selNodes.length == 1) {
28297                                 dragData.ddel = target.cloneNode(true); // the div element
28298                         } else {
28299                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28300                                 div.className = 'multi-proxy';
28301                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28302                                         div.appendChild(selNodes[i].cloneNode(true));
28303                                 }
28304                                 dragData.ddel = div;
28305                         }
28306             //console.log(dragData)
28307             //console.log(dragData.ddel.innerHTML)
28308                         return dragData;
28309                 }
28310         //console.log('nodragData')
28311                 return false;
28312     },
28313     
28314 /**     Specify to which ddGroup items in this DDView may be dragged. */
28315     setDraggable: function(ddGroup) {
28316         if (ddGroup instanceof Array) {
28317                 Roo.each(ddGroup, this.setDraggable, this);
28318                 return;
28319         }
28320         if (this.dragZone) {
28321                 this.dragZone.addToGroup(ddGroup);
28322         } else {
28323                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28324                                 containerScroll: true,
28325                                 ddGroup: ddGroup 
28326
28327                         });
28328 //                      Draggability implies selection. DragZone's mousedown selects the element.
28329                         if (!this.multiSelect) { this.singleSelect = true; }
28330
28331 //                      Wire the DragZone's handlers up to methods in *this*
28332                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28333                 }
28334     },
28335
28336 /**     Specify from which ddGroup this DDView accepts drops. */
28337     setDroppable: function(ddGroup) {
28338         if (ddGroup instanceof Array) {
28339                 Roo.each(ddGroup, this.setDroppable, this);
28340                 return;
28341         }
28342         if (this.dropZone) {
28343                 this.dropZone.addToGroup(ddGroup);
28344         } else {
28345                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28346                                 containerScroll: true,
28347                                 ddGroup: ddGroup
28348                         });
28349
28350 //                      Wire the DropZone's handlers up to methods in *this*
28351                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28352                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28353                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28354                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28355                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28356                 }
28357     },
28358
28359 /**     Decide whether to drop above or below a View node. */
28360     getDropPoint : function(e, n, dd){
28361         if (n == this.el.dom) { return "above"; }
28362                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28363                 var c = t + (b - t) / 2;
28364                 var y = Roo.lib.Event.getPageY(e);
28365                 if(y <= c) {
28366                         return "above";
28367                 }else{
28368                         return "below";
28369                 }
28370     },
28371
28372     onNodeEnter : function(n, dd, e, data){
28373                 return false;
28374     },
28375     
28376     onNodeOver : function(n, dd, e, data){
28377                 var pt = this.getDropPoint(e, n, dd);
28378                 // set the insert point style on the target node
28379                 var dragElClass = this.dropNotAllowed;
28380                 if (pt) {
28381                         var targetElClass;
28382                         if (pt == "above"){
28383                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28384                                 targetElClass = "x-view-drag-insert-above";
28385                         } else {
28386                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28387                                 targetElClass = "x-view-drag-insert-below";
28388                         }
28389                         if (this.lastInsertClass != targetElClass){
28390                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28391                                 this.lastInsertClass = targetElClass;
28392                         }
28393                 }
28394                 return dragElClass;
28395         },
28396
28397     onNodeOut : function(n, dd, e, data){
28398                 this.removeDropIndicators(n);
28399     },
28400
28401     onNodeDrop : function(n, dd, e, data){
28402         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28403                 return false;
28404         }
28405         var pt = this.getDropPoint(e, n, dd);
28406                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28407                 if (pt == "below") { insertAt++; }
28408                 for (var i = 0; i < data.records.length; i++) {
28409                         var r = data.records[i];
28410                         var dup = this.store.getById(r.id);
28411                         if (dup && (dd != this.dragZone)) {
28412                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28413                         } else {
28414                                 if (data.copy) {
28415                                         this.store.insert(insertAt++, r.copy());
28416                                 } else {
28417                                         data.source.isDirtyFlag = true;
28418                                         r.store.remove(r);
28419                                         this.store.insert(insertAt++, r);
28420                                 }
28421                                 this.isDirtyFlag = true;
28422                         }
28423                 }
28424                 this.dragZone.cachedTarget = null;
28425                 return true;
28426     },
28427
28428     removeDropIndicators : function(n){
28429                 if(n){
28430                         Roo.fly(n).removeClass([
28431                                 "x-view-drag-insert-above",
28432                                 "x-view-drag-insert-below"]);
28433                         this.lastInsertClass = "_noclass";
28434                 }
28435     },
28436
28437 /**
28438  *      Utility method. Add a delete option to the DDView's context menu.
28439  *      @param {String} imageUrl The URL of the "delete" icon image.
28440  */
28441         setDeletable: function(imageUrl) {
28442                 if (!this.singleSelect && !this.multiSelect) {
28443                         this.singleSelect = true;
28444                 }
28445                 var c = this.getContextMenu();
28446                 this.contextMenu.on("itemclick", function(item) {
28447                         switch (item.id) {
28448                                 case "delete":
28449                                         this.remove(this.getSelectedIndexes());
28450                                         break;
28451                         }
28452                 }, this);
28453                 this.contextMenu.add({
28454                         icon: imageUrl,
28455                         id: "delete",
28456                         text: 'Delete'
28457                 });
28458         },
28459         
28460 /**     Return the context menu for this DDView. */
28461         getContextMenu: function() {
28462                 if (!this.contextMenu) {
28463 //                      Create the View's context menu
28464                         this.contextMenu = new Roo.menu.Menu({
28465                                 id: this.id + "-contextmenu"
28466                         });
28467                         this.el.on("contextmenu", this.showContextMenu, this);
28468                 }
28469                 return this.contextMenu;
28470         },
28471         
28472         disableContextMenu: function() {
28473                 if (this.contextMenu) {
28474                         this.el.un("contextmenu", this.showContextMenu, this);
28475                 }
28476         },
28477
28478         showContextMenu: function(e, item) {
28479         item = this.findItemFromChild(e.getTarget());
28480                 if (item) {
28481                         e.stopEvent();
28482                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28483                         this.contextMenu.showAt(e.getXY());
28484             }
28485     },
28486
28487 /**
28488  *      Remove {@link Roo.data.Record}s at the specified indices.
28489  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28490  */
28491     remove: function(selectedIndices) {
28492                 selectedIndices = [].concat(selectedIndices);
28493                 for (var i = 0; i < selectedIndices.length; i++) {
28494                         var rec = this.store.getAt(selectedIndices[i]);
28495                         this.store.remove(rec);
28496                 }
28497     },
28498
28499 /**
28500  *      Double click fires the event, but also, if this is draggable, and there is only one other
28501  *      related DropZone, it transfers the selected node.
28502  */
28503     onDblClick : function(e){
28504         var item = this.findItemFromChild(e.getTarget());
28505         if(item){
28506             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28507                 return false;
28508             }
28509             if (this.dragGroup) {
28510                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28511                     while (targets.indexOf(this.dropZone) > -1) {
28512                             targets.remove(this.dropZone);
28513                                 }
28514                     if (targets.length == 1) {
28515                                         this.dragZone.cachedTarget = null;
28516                         var el = Roo.get(targets[0].getEl());
28517                         var box = el.getBox(true);
28518                         targets[0].onNodeDrop(el.dom, {
28519                                 target: el.dom,
28520                                 xy: [box.x, box.y + box.height - 1]
28521                         }, null, this.getDragData(e));
28522                     }
28523                 }
28524         }
28525     },
28526     
28527     handleSelection: function(e) {
28528                 this.dragZone.cachedTarget = null;
28529         var item = this.findItemFromChild(e.getTarget());
28530         if (!item) {
28531                 this.clearSelections(true);
28532                 return;
28533         }
28534                 if (item && (this.multiSelect || this.singleSelect)){
28535                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28536                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28537                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28538                                 this.unselect(item);
28539                         } else {
28540                                 this.select(item, this.multiSelect && e.ctrlKey);
28541                                 this.lastSelection = item;
28542                         }
28543                 }
28544     },
28545
28546     onItemClick : function(item, index, e){
28547                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28548                         return false;
28549                 }
28550                 return true;
28551     },
28552
28553     unselect : function(nodeInfo, suppressEvent){
28554                 var node = this.getNode(nodeInfo);
28555                 if(node && this.isSelected(node)){
28556                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28557                                 Roo.fly(node).removeClass(this.selectedClass);
28558                                 this.selections.remove(node);
28559                                 if(!suppressEvent){
28560                                         this.fireEvent("selectionchange", this, this.selections);
28561                                 }
28562                         }
28563                 }
28564     }
28565 });
28566 /*
28567  * Based on:
28568  * Ext JS Library 1.1.1
28569  * Copyright(c) 2006-2007, Ext JS, LLC.
28570  *
28571  * Originally Released Under LGPL - original licence link has changed is not relivant.
28572  *
28573  * Fork - LGPL
28574  * <script type="text/javascript">
28575  */
28576  
28577 /**
28578  * @class Roo.LayoutManager
28579  * @extends Roo.util.Observable
28580  * Base class for layout managers.
28581  */
28582 Roo.LayoutManager = function(container, config){
28583     Roo.LayoutManager.superclass.constructor.call(this);
28584     this.el = Roo.get(container);
28585     // ie scrollbar fix
28586     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28587         document.body.scroll = "no";
28588     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28589         this.el.position('relative');
28590     }
28591     this.id = this.el.id;
28592     this.el.addClass("x-layout-container");
28593     /** false to disable window resize monitoring @type Boolean */
28594     this.monitorWindowResize = true;
28595     this.regions = {};
28596     this.addEvents({
28597         /**
28598          * @event layout
28599          * Fires when a layout is performed. 
28600          * @param {Roo.LayoutManager} this
28601          */
28602         "layout" : true,
28603         /**
28604          * @event regionresized
28605          * Fires when the user resizes a region. 
28606          * @param {Roo.LayoutRegion} region The resized region
28607          * @param {Number} newSize The new size (width for east/west, height for north/south)
28608          */
28609         "regionresized" : true,
28610         /**
28611          * @event regioncollapsed
28612          * Fires when a region is collapsed. 
28613          * @param {Roo.LayoutRegion} region The collapsed region
28614          */
28615         "regioncollapsed" : true,
28616         /**
28617          * @event regionexpanded
28618          * Fires when a region is expanded.  
28619          * @param {Roo.LayoutRegion} region The expanded region
28620          */
28621         "regionexpanded" : true
28622     });
28623     this.updating = false;
28624     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28625 };
28626
28627 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28628     /**
28629      * Returns true if this layout is currently being updated
28630      * @return {Boolean}
28631      */
28632     isUpdating : function(){
28633         return this.updating; 
28634     },
28635     
28636     /**
28637      * Suspend the LayoutManager from doing auto-layouts while
28638      * making multiple add or remove calls
28639      */
28640     beginUpdate : function(){
28641         this.updating = true;    
28642     },
28643     
28644     /**
28645      * Restore auto-layouts and optionally disable the manager from performing a layout
28646      * @param {Boolean} noLayout true to disable a layout update 
28647      */
28648     endUpdate : function(noLayout){
28649         this.updating = false;
28650         if(!noLayout){
28651             this.layout();
28652         }    
28653     },
28654     
28655     layout: function(){
28656         
28657     },
28658     
28659     onRegionResized : function(region, newSize){
28660         this.fireEvent("regionresized", region, newSize);
28661         this.layout();
28662     },
28663     
28664     onRegionCollapsed : function(region){
28665         this.fireEvent("regioncollapsed", region);
28666     },
28667     
28668     onRegionExpanded : function(region){
28669         this.fireEvent("regionexpanded", region);
28670     },
28671         
28672     /**
28673      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28674      * performs box-model adjustments.
28675      * @return {Object} The size as an object {width: (the width), height: (the height)}
28676      */
28677     getViewSize : function(){
28678         var size;
28679         if(this.el.dom != document.body){
28680             size = this.el.getSize();
28681         }else{
28682             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28683         }
28684         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28685         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28686         return size;
28687     },
28688     
28689     /**
28690      * Returns the Element this layout is bound to.
28691      * @return {Roo.Element}
28692      */
28693     getEl : function(){
28694         return this.el;
28695     },
28696     
28697     /**
28698      * Returns the specified region.
28699      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28700      * @return {Roo.LayoutRegion}
28701      */
28702     getRegion : function(target){
28703         return this.regions[target.toLowerCase()];
28704     },
28705     
28706     onWindowResize : function(){
28707         if(this.monitorWindowResize){
28708             this.layout();
28709         }
28710     }
28711 });/*
28712  * Based on:
28713  * Ext JS Library 1.1.1
28714  * Copyright(c) 2006-2007, Ext JS, LLC.
28715  *
28716  * Originally Released Under LGPL - original licence link has changed is not relivant.
28717  *
28718  * Fork - LGPL
28719  * <script type="text/javascript">
28720  */
28721 /**
28722  * @class Roo.BorderLayout
28723  * @extends Roo.LayoutManager
28724  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28725  * please see: <br><br>
28726  * <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>
28727  * <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>
28728  * Example:
28729  <pre><code>
28730  var layout = new Roo.BorderLayout(document.body, {
28731     north: {
28732         initialSize: 25,
28733         titlebar: false
28734     },
28735     west: {
28736         split:true,
28737         initialSize: 200,
28738         minSize: 175,
28739         maxSize: 400,
28740         titlebar: true,
28741         collapsible: true
28742     },
28743     east: {
28744         split:true,
28745         initialSize: 202,
28746         minSize: 175,
28747         maxSize: 400,
28748         titlebar: true,
28749         collapsible: true
28750     },
28751     south: {
28752         split:true,
28753         initialSize: 100,
28754         minSize: 100,
28755         maxSize: 200,
28756         titlebar: true,
28757         collapsible: true
28758     },
28759     center: {
28760         titlebar: true,
28761         autoScroll:true,
28762         resizeTabs: true,
28763         minTabWidth: 50,
28764         preferredTabWidth: 150
28765     }
28766 });
28767
28768 // shorthand
28769 var CP = Roo.ContentPanel;
28770
28771 layout.beginUpdate();
28772 layout.add("north", new CP("north", "North"));
28773 layout.add("south", new CP("south", {title: "South", closable: true}));
28774 layout.add("west", new CP("west", {title: "West"}));
28775 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28776 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28777 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28778 layout.getRegion("center").showPanel("center1");
28779 layout.endUpdate();
28780 </code></pre>
28781
28782 <b>The container the layout is rendered into can be either the body element or any other element.
28783 If it is not the body element, the container needs to either be an absolute positioned element,
28784 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28785 the container size if it is not the body element.</b>
28786
28787 * @constructor
28788 * Create a new BorderLayout
28789 * @param {String/HTMLElement/Element} container The container this layout is bound to
28790 * @param {Object} config Configuration options
28791  */
28792 Roo.BorderLayout = function(container, config){
28793     config = config || {};
28794     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28795     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28796     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28797         var target = this.factory.validRegions[i];
28798         if(config[target]){
28799             this.addRegion(target, config[target]);
28800         }
28801     }
28802 };
28803
28804 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28805     /**
28806      * Creates and adds a new region if it doesn't already exist.
28807      * @param {String} target The target region key (north, south, east, west or center).
28808      * @param {Object} config The regions config object
28809      * @return {BorderLayoutRegion} The new region
28810      */
28811     addRegion : function(target, config){
28812         if(!this.regions[target]){
28813             var r = this.factory.create(target, this, config);
28814             this.bindRegion(target, r);
28815         }
28816         return this.regions[target];
28817     },
28818
28819     // private (kinda)
28820     bindRegion : function(name, r){
28821         this.regions[name] = r;
28822         r.on("visibilitychange", this.layout, this);
28823         r.on("paneladded", this.layout, this);
28824         r.on("panelremoved", this.layout, this);
28825         r.on("invalidated", this.layout, this);
28826         r.on("resized", this.onRegionResized, this);
28827         r.on("collapsed", this.onRegionCollapsed, this);
28828         r.on("expanded", this.onRegionExpanded, this);
28829     },
28830
28831     /**
28832      * Performs a layout update.
28833      */
28834     layout : function(){
28835         if(this.updating) return;
28836         var size = this.getViewSize();
28837         var w = size.width;
28838         var h = size.height;
28839         var centerW = w;
28840         var centerH = h;
28841         var centerY = 0;
28842         var centerX = 0;
28843         //var x = 0, y = 0;
28844
28845         var rs = this.regions;
28846         var north = rs["north"];
28847         var south = rs["south"]; 
28848         var west = rs["west"];
28849         var east = rs["east"];
28850         var center = rs["center"];
28851         //if(this.hideOnLayout){ // not supported anymore
28852             //c.el.setStyle("display", "none");
28853         //}
28854         if(north && north.isVisible()){
28855             var b = north.getBox();
28856             var m = north.getMargins();
28857             b.width = w - (m.left+m.right);
28858             b.x = m.left;
28859             b.y = m.top;
28860             centerY = b.height + b.y + m.bottom;
28861             centerH -= centerY;
28862             north.updateBox(this.safeBox(b));
28863         }
28864         if(south && south.isVisible()){
28865             var b = south.getBox();
28866             var m = south.getMargins();
28867             b.width = w - (m.left+m.right);
28868             b.x = m.left;
28869             var totalHeight = (b.height + m.top + m.bottom);
28870             b.y = h - totalHeight + m.top;
28871             centerH -= totalHeight;
28872             south.updateBox(this.safeBox(b));
28873         }
28874         if(west && west.isVisible()){
28875             var b = west.getBox();
28876             var m = west.getMargins();
28877             b.height = centerH - (m.top+m.bottom);
28878             b.x = m.left;
28879             b.y = centerY + m.top;
28880             var totalWidth = (b.width + m.left + m.right);
28881             centerX += totalWidth;
28882             centerW -= totalWidth;
28883             west.updateBox(this.safeBox(b));
28884         }
28885         if(east && east.isVisible()){
28886             var b = east.getBox();
28887             var m = east.getMargins();
28888             b.height = centerH - (m.top+m.bottom);
28889             var totalWidth = (b.width + m.left + m.right);
28890             b.x = w - totalWidth + m.left;
28891             b.y = centerY + m.top;
28892             centerW -= totalWidth;
28893             east.updateBox(this.safeBox(b));
28894         }
28895         if(center){
28896             var m = center.getMargins();
28897             var centerBox = {
28898                 x: centerX + m.left,
28899                 y: centerY + m.top,
28900                 width: centerW - (m.left+m.right),
28901                 height: centerH - (m.top+m.bottom)
28902             };
28903             //if(this.hideOnLayout){
28904                 //center.el.setStyle("display", "block");
28905             //}
28906             center.updateBox(this.safeBox(centerBox));
28907         }
28908         this.el.repaint();
28909         this.fireEvent("layout", this);
28910     },
28911
28912     // private
28913     safeBox : function(box){
28914         box.width = Math.max(0, box.width);
28915         box.height = Math.max(0, box.height);
28916         return box;
28917     },
28918
28919     /**
28920      * Adds a ContentPanel (or subclass) to this layout.
28921      * @param {String} target The target region key (north, south, east, west or center).
28922      * @param {Roo.ContentPanel} panel The panel to add
28923      * @return {Roo.ContentPanel} The added panel
28924      */
28925     add : function(target, panel){
28926          
28927         target = target.toLowerCase();
28928         return this.regions[target].add(panel);
28929     },
28930
28931     /**
28932      * Remove a ContentPanel (or subclass) to this layout.
28933      * @param {String} target The target region key (north, south, east, west or center).
28934      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28935      * @return {Roo.ContentPanel} The removed panel
28936      */
28937     remove : function(target, panel){
28938         target = target.toLowerCase();
28939         return this.regions[target].remove(panel);
28940     },
28941
28942     /**
28943      * Searches all regions for a panel with the specified id
28944      * @param {String} panelId
28945      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28946      */
28947     findPanel : function(panelId){
28948         var rs = this.regions;
28949         for(var target in rs){
28950             if(typeof rs[target] != "function"){
28951                 var p = rs[target].getPanel(panelId);
28952                 if(p){
28953                     return p;
28954                 }
28955             }
28956         }
28957         return null;
28958     },
28959
28960     /**
28961      * Searches all regions for a panel with the specified id and activates (shows) it.
28962      * @param {String/ContentPanel} panelId The panels id or the panel itself
28963      * @return {Roo.ContentPanel} The shown panel or null
28964      */
28965     showPanel : function(panelId) {
28966       var rs = this.regions;
28967       for(var target in rs){
28968          var r = rs[target];
28969          if(typeof r != "function"){
28970             if(r.hasPanel(panelId)){
28971                return r.showPanel(panelId);
28972             }
28973          }
28974       }
28975       return null;
28976    },
28977
28978    /**
28979      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28980      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28981      */
28982     restoreState : function(provider){
28983         if(!provider){
28984             provider = Roo.state.Manager;
28985         }
28986         var sm = new Roo.LayoutStateManager();
28987         sm.init(this, provider);
28988     },
28989
28990     /**
28991      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28992      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28993      * a valid ContentPanel config object.  Example:
28994      * <pre><code>
28995 // Create the main layout
28996 var layout = new Roo.BorderLayout('main-ct', {
28997     west: {
28998         split:true,
28999         minSize: 175,
29000         titlebar: true
29001     },
29002     center: {
29003         title:'Components'
29004     }
29005 }, 'main-ct');
29006
29007 // Create and add multiple ContentPanels at once via configs
29008 layout.batchAdd({
29009    west: {
29010        id: 'source-files',
29011        autoCreate:true,
29012        title:'Ext Source Files',
29013        autoScroll:true,
29014        fitToFrame:true
29015    },
29016    center : {
29017        el: cview,
29018        autoScroll:true,
29019        fitToFrame:true,
29020        toolbar: tb,
29021        resizeEl:'cbody'
29022    }
29023 });
29024 </code></pre>
29025      * @param {Object} regions An object containing ContentPanel configs by region name
29026      */
29027     batchAdd : function(regions){
29028         this.beginUpdate();
29029         for(var rname in regions){
29030             var lr = this.regions[rname];
29031             if(lr){
29032                 this.addTypedPanels(lr, regions[rname]);
29033             }
29034         }
29035         this.endUpdate();
29036     },
29037
29038     // private
29039     addTypedPanels : function(lr, ps){
29040         if(typeof ps == 'string'){
29041             lr.add(new Roo.ContentPanel(ps));
29042         }
29043         else if(ps instanceof Array){
29044             for(var i =0, len = ps.length; i < len; i++){
29045                 this.addTypedPanels(lr, ps[i]);
29046             }
29047         }
29048         else if(!ps.events){ // raw config?
29049             var el = ps.el;
29050             delete ps.el; // prevent conflict
29051             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29052         }
29053         else {  // panel object assumed!
29054             lr.add(ps);
29055         }
29056     },
29057     /**
29058      * Adds a xtype elements to the layout.
29059      * <pre><code>
29060
29061 layout.addxtype({
29062        xtype : 'ContentPanel',
29063        region: 'west',
29064        items: [ .... ]
29065    }
29066 );
29067
29068 layout.addxtype({
29069         xtype : 'NestedLayoutPanel',
29070         region: 'west',
29071         layout: {
29072            center: { },
29073            west: { }   
29074         },
29075         items : [ ... list of content panels or nested layout panels.. ]
29076    }
29077 );
29078 </code></pre>
29079      * @param {Object} cfg Xtype definition of item to add.
29080      */
29081     addxtype : function(cfg)
29082     {
29083         // basically accepts a pannel...
29084         // can accept a layout region..!?!?
29085        // console.log('BorderLayout add ' + cfg.xtype)
29086         
29087         if (!cfg.xtype.match(/Panel$/)) {
29088             return false;
29089         }
29090         var ret = false;
29091         var region = cfg.region;
29092         delete cfg.region;
29093         
29094           
29095         var xitems = [];
29096         if (cfg.items) {
29097             xitems = cfg.items;
29098             delete cfg.items;
29099         }
29100         
29101         
29102         switch(cfg.xtype) 
29103         {
29104             case 'ContentPanel':  // ContentPanel (el, cfg)
29105             case 'ScrollPanel':  // ContentPanel (el, cfg)
29106                 if(cfg.autoCreate) {
29107                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29108                 } else {
29109                     var el = this.el.createChild();
29110                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29111                 }
29112                 
29113                 this.add(region, ret);
29114                 break;
29115             
29116             
29117             case 'TreePanel': // our new panel!
29118                 cfg.el = this.el.createChild();
29119                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29120                 this.add(region, ret);
29121                 break;
29122             
29123             case 'NestedLayoutPanel': 
29124                 // create a new Layout (which is  a Border Layout...
29125                 var el = this.el.createChild();
29126                 var clayout = cfg.layout;
29127                 delete cfg.layout;
29128                 clayout.items   = clayout.items  || [];
29129                 // replace this exitems with the clayout ones..
29130                 xitems = clayout.items;
29131                  
29132                 
29133                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29134                     cfg.background = false;
29135                 }
29136                 var layout = new Roo.BorderLayout(el, clayout);
29137                 
29138                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29139                 //console.log('adding nested layout panel '  + cfg.toSource());
29140                 this.add(region, ret);
29141                 
29142                 break;
29143                 
29144             case 'GridPanel': 
29145             
29146                 // needs grid and region
29147                 
29148                 //var el = this.getRegion(region).el.createChild();
29149                 var el = this.el.createChild();
29150                 // create the grid first...
29151                 
29152                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29153                 delete cfg.grid;
29154                 if (region == 'center' && this.active ) {
29155                     cfg.background = false;
29156                 }
29157                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29158                 
29159                 this.add(region, ret);
29160                 if (cfg.background) {
29161                     ret.on('activate', function(gp) {
29162                         if (!gp.grid.rendered) {
29163                             gp.grid.render();
29164                         }
29165                     });
29166                 } else {
29167                     grid.render();
29168                 }
29169                 break;
29170            
29171                
29172                 
29173                 
29174             default: 
29175                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29176                 return;
29177              // GridPanel (grid, cfg)
29178             
29179         }
29180         this.beginUpdate();
29181         // add children..
29182         Roo.each(xitems, function(i)  {
29183             ret.addxtype(i);
29184         });
29185         this.endUpdate();
29186         return ret;
29187         
29188     }
29189 });
29190
29191 /**
29192  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29193  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29194  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29195  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29196  * <pre><code>
29197 // shorthand
29198 var CP = Roo.ContentPanel;
29199
29200 var layout = Roo.BorderLayout.create({
29201     north: {
29202         initialSize: 25,
29203         titlebar: false,
29204         panels: [new CP("north", "North")]
29205     },
29206     west: {
29207         split:true,
29208         initialSize: 200,
29209         minSize: 175,
29210         maxSize: 400,
29211         titlebar: true,
29212         collapsible: true,
29213         panels: [new CP("west", {title: "West"})]
29214     },
29215     east: {
29216         split:true,
29217         initialSize: 202,
29218         minSize: 175,
29219         maxSize: 400,
29220         titlebar: true,
29221         collapsible: true,
29222         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29223     },
29224     south: {
29225         split:true,
29226         initialSize: 100,
29227         minSize: 100,
29228         maxSize: 200,
29229         titlebar: true,
29230         collapsible: true,
29231         panels: [new CP("south", {title: "South", closable: true})]
29232     },
29233     center: {
29234         titlebar: true,
29235         autoScroll:true,
29236         resizeTabs: true,
29237         minTabWidth: 50,
29238         preferredTabWidth: 150,
29239         panels: [
29240             new CP("center1", {title: "Close Me", closable: true}),
29241             new CP("center2", {title: "Center Panel", closable: false})
29242         ]
29243     }
29244 }, document.body);
29245
29246 layout.getRegion("center").showPanel("center1");
29247 </code></pre>
29248  * @param config
29249  * @param targetEl
29250  */
29251 Roo.BorderLayout.create = function(config, targetEl){
29252     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29253     layout.beginUpdate();
29254     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29255     for(var j = 0, jlen = regions.length; j < jlen; j++){
29256         var lr = regions[j];
29257         if(layout.regions[lr] && config[lr].panels){
29258             var r = layout.regions[lr];
29259             var ps = config[lr].panels;
29260             layout.addTypedPanels(r, ps);
29261         }
29262     }
29263     layout.endUpdate();
29264     return layout;
29265 };
29266
29267 // private
29268 Roo.BorderLayout.RegionFactory = {
29269     // private
29270     validRegions : ["north","south","east","west","center"],
29271
29272     // private
29273     create : function(target, mgr, config){
29274         target = target.toLowerCase();
29275         if(config.lightweight || config.basic){
29276             return new Roo.BasicLayoutRegion(mgr, config, target);
29277         }
29278         switch(target){
29279             case "north":
29280                 return new Roo.NorthLayoutRegion(mgr, config);
29281             case "south":
29282                 return new Roo.SouthLayoutRegion(mgr, config);
29283             case "east":
29284                 return new Roo.EastLayoutRegion(mgr, config);
29285             case "west":
29286                 return new Roo.WestLayoutRegion(mgr, config);
29287             case "center":
29288                 return new Roo.CenterLayoutRegion(mgr, config);
29289         }
29290         throw 'Layout region "'+target+'" not supported.';
29291     }
29292 };/*
29293  * Based on:
29294  * Ext JS Library 1.1.1
29295  * Copyright(c) 2006-2007, Ext JS, LLC.
29296  *
29297  * Originally Released Under LGPL - original licence link has changed is not relivant.
29298  *
29299  * Fork - LGPL
29300  * <script type="text/javascript">
29301  */
29302  
29303 /**
29304  * @class Roo.BasicLayoutRegion
29305  * @extends Roo.util.Observable
29306  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29307  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29308  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29309  */
29310 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29311     this.mgr = mgr;
29312     this.position  = pos;
29313     this.events = {
29314         /**
29315          * @scope Roo.BasicLayoutRegion
29316          */
29317         
29318         /**
29319          * @event beforeremove
29320          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29321          * @param {Roo.LayoutRegion} this
29322          * @param {Roo.ContentPanel} panel The panel
29323          * @param {Object} e The cancel event object
29324          */
29325         "beforeremove" : true,
29326         /**
29327          * @event invalidated
29328          * Fires when the layout for this region is changed.
29329          * @param {Roo.LayoutRegion} this
29330          */
29331         "invalidated" : true,
29332         /**
29333          * @event visibilitychange
29334          * Fires when this region is shown or hidden 
29335          * @param {Roo.LayoutRegion} this
29336          * @param {Boolean} visibility true or false
29337          */
29338         "visibilitychange" : true,
29339         /**
29340          * @event paneladded
29341          * Fires when a panel is added. 
29342          * @param {Roo.LayoutRegion} this
29343          * @param {Roo.ContentPanel} panel The panel
29344          */
29345         "paneladded" : true,
29346         /**
29347          * @event panelremoved
29348          * Fires when a panel is removed. 
29349          * @param {Roo.LayoutRegion} this
29350          * @param {Roo.ContentPanel} panel The panel
29351          */
29352         "panelremoved" : true,
29353         /**
29354          * @event collapsed
29355          * Fires when this region is collapsed.
29356          * @param {Roo.LayoutRegion} this
29357          */
29358         "collapsed" : true,
29359         /**
29360          * @event expanded
29361          * Fires when this region is expanded.
29362          * @param {Roo.LayoutRegion} this
29363          */
29364         "expanded" : true,
29365         /**
29366          * @event slideshow
29367          * Fires when this region is slid into view.
29368          * @param {Roo.LayoutRegion} this
29369          */
29370         "slideshow" : true,
29371         /**
29372          * @event slidehide
29373          * Fires when this region slides out of view. 
29374          * @param {Roo.LayoutRegion} this
29375          */
29376         "slidehide" : true,
29377         /**
29378          * @event panelactivated
29379          * Fires when a panel is activated. 
29380          * @param {Roo.LayoutRegion} this
29381          * @param {Roo.ContentPanel} panel The activated panel
29382          */
29383         "panelactivated" : true,
29384         /**
29385          * @event resized
29386          * Fires when the user resizes this region. 
29387          * @param {Roo.LayoutRegion} this
29388          * @param {Number} newSize The new size (width for east/west, height for north/south)
29389          */
29390         "resized" : true
29391     };
29392     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29393     this.panels = new Roo.util.MixedCollection();
29394     this.panels.getKey = this.getPanelId.createDelegate(this);
29395     this.box = null;
29396     this.activePanel = null;
29397     // ensure listeners are added...
29398     
29399     if (config.listeners || config.events) {
29400         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29401             listeners : config.listeners || {},
29402             events : config.events || {}
29403         });
29404     }
29405     
29406     if(skipConfig !== true){
29407         this.applyConfig(config);
29408     }
29409 };
29410
29411 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29412     getPanelId : function(p){
29413         return p.getId();
29414     },
29415     
29416     applyConfig : function(config){
29417         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29418         this.config = config;
29419         
29420     },
29421     
29422     /**
29423      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29424      * the width, for horizontal (north, south) the height.
29425      * @param {Number} newSize The new width or height
29426      */
29427     resizeTo : function(newSize){
29428         var el = this.el ? this.el :
29429                  (this.activePanel ? this.activePanel.getEl() : null);
29430         if(el){
29431             switch(this.position){
29432                 case "east":
29433                 case "west":
29434                     el.setWidth(newSize);
29435                     this.fireEvent("resized", this, newSize);
29436                 break;
29437                 case "north":
29438                 case "south":
29439                     el.setHeight(newSize);
29440                     this.fireEvent("resized", this, newSize);
29441                 break;                
29442             }
29443         }
29444     },
29445     
29446     getBox : function(){
29447         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29448     },
29449     
29450     getMargins : function(){
29451         return this.margins;
29452     },
29453     
29454     updateBox : function(box){
29455         this.box = box;
29456         var el = this.activePanel.getEl();
29457         el.dom.style.left = box.x + "px";
29458         el.dom.style.top = box.y + "px";
29459         this.activePanel.setSize(box.width, box.height);
29460     },
29461     
29462     /**
29463      * Returns the container element for this region.
29464      * @return {Roo.Element}
29465      */
29466     getEl : function(){
29467         return this.activePanel;
29468     },
29469     
29470     /**
29471      * Returns true if this region is currently visible.
29472      * @return {Boolean}
29473      */
29474     isVisible : function(){
29475         return this.activePanel ? true : false;
29476     },
29477     
29478     setActivePanel : function(panel){
29479         panel = this.getPanel(panel);
29480         if(this.activePanel && this.activePanel != panel){
29481             this.activePanel.setActiveState(false);
29482             this.activePanel.getEl().setLeftTop(-10000,-10000);
29483         }
29484         this.activePanel = panel;
29485         panel.setActiveState(true);
29486         if(this.box){
29487             panel.setSize(this.box.width, this.box.height);
29488         }
29489         this.fireEvent("panelactivated", this, panel);
29490         this.fireEvent("invalidated");
29491     },
29492     
29493     /**
29494      * Show the specified panel.
29495      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29496      * @return {Roo.ContentPanel} The shown panel or null
29497      */
29498     showPanel : function(panel){
29499         if(panel = this.getPanel(panel)){
29500             this.setActivePanel(panel);
29501         }
29502         return panel;
29503     },
29504     
29505     /**
29506      * Get the active panel for this region.
29507      * @return {Roo.ContentPanel} The active panel or null
29508      */
29509     getActivePanel : function(){
29510         return this.activePanel;
29511     },
29512     
29513     /**
29514      * Add the passed ContentPanel(s)
29515      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29516      * @return {Roo.ContentPanel} The panel added (if only one was added)
29517      */
29518     add : function(panel){
29519         if(arguments.length > 1){
29520             for(var i = 0, len = arguments.length; i < len; i++) {
29521                 this.add(arguments[i]);
29522             }
29523             return null;
29524         }
29525         if(this.hasPanel(panel)){
29526             this.showPanel(panel);
29527             return panel;
29528         }
29529         var el = panel.getEl();
29530         if(el.dom.parentNode != this.mgr.el.dom){
29531             this.mgr.el.dom.appendChild(el.dom);
29532         }
29533         if(panel.setRegion){
29534             panel.setRegion(this);
29535         }
29536         this.panels.add(panel);
29537         el.setStyle("position", "absolute");
29538         if(!panel.background){
29539             this.setActivePanel(panel);
29540             if(this.config.initialSize && this.panels.getCount()==1){
29541                 this.resizeTo(this.config.initialSize);
29542             }
29543         }
29544         this.fireEvent("paneladded", this, panel);
29545         return panel;
29546     },
29547     
29548     /**
29549      * Returns true if the panel is in this region.
29550      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29551      * @return {Boolean}
29552      */
29553     hasPanel : function(panel){
29554         if(typeof panel == "object"){ // must be panel obj
29555             panel = panel.getId();
29556         }
29557         return this.getPanel(panel) ? true : false;
29558     },
29559     
29560     /**
29561      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29562      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29563      * @param {Boolean} preservePanel Overrides the config preservePanel option
29564      * @return {Roo.ContentPanel} The panel that was removed
29565      */
29566     remove : function(panel, preservePanel){
29567         panel = this.getPanel(panel);
29568         if(!panel){
29569             return null;
29570         }
29571         var e = {};
29572         this.fireEvent("beforeremove", this, panel, e);
29573         if(e.cancel === true){
29574             return null;
29575         }
29576         var panelId = panel.getId();
29577         this.panels.removeKey(panelId);
29578         return panel;
29579     },
29580     
29581     /**
29582      * Returns the panel specified or null if it's not in this region.
29583      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29584      * @return {Roo.ContentPanel}
29585      */
29586     getPanel : function(id){
29587         if(typeof id == "object"){ // must be panel obj
29588             return id;
29589         }
29590         return this.panels.get(id);
29591     },
29592     
29593     /**
29594      * Returns this regions position (north/south/east/west/center).
29595      * @return {String} 
29596      */
29597     getPosition: function(){
29598         return this.position;    
29599     }
29600 });/*
29601  * Based on:
29602  * Ext JS Library 1.1.1
29603  * Copyright(c) 2006-2007, Ext JS, LLC.
29604  *
29605  * Originally Released Under LGPL - original licence link has changed is not relivant.
29606  *
29607  * Fork - LGPL
29608  * <script type="text/javascript">
29609  */
29610  
29611 /**
29612  * @class Roo.LayoutRegion
29613  * @extends Roo.BasicLayoutRegion
29614  * This class represents a region in a layout manager.
29615  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29616  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29617  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29618  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29619  * @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})
29620  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29621  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29622  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29623  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29624  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29625  * @cfg {String} title The title for the region (overrides panel titles)
29626  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29627  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29628  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29629  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29630  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29631  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29632  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29633  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29634  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29635  * @cfg {Boolean} showPin True to show a pin button
29636 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29637 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29638 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29639 * @cfg {Number} width  For East/West panels
29640 * @cfg {Number} height For North/South panels
29641 * @cfg {Boolean} split To show the splitter
29642  */
29643 Roo.LayoutRegion = function(mgr, config, pos){
29644     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29645     var dh = Roo.DomHelper;
29646     /** This region's container element 
29647     * @type Roo.Element */
29648     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29649     /** This region's title element 
29650     * @type Roo.Element */
29651
29652     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29653         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29654         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29655     ]}, true);
29656     this.titleEl.enableDisplayMode();
29657     /** This region's title text element 
29658     * @type HTMLElement */
29659     this.titleTextEl = this.titleEl.dom.firstChild;
29660     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29661     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29662     this.closeBtn.enableDisplayMode();
29663     this.closeBtn.on("click", this.closeClicked, this);
29664     this.closeBtn.hide();
29665
29666     this.createBody(config);
29667     this.visible = true;
29668     this.collapsed = false;
29669
29670     if(config.hideWhenEmpty){
29671         this.hide();
29672         this.on("paneladded", this.validateVisibility, this);
29673         this.on("panelremoved", this.validateVisibility, this);
29674     }
29675     this.applyConfig(config);
29676 };
29677
29678 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29679
29680     createBody : function(){
29681         /** This region's body element 
29682         * @type Roo.Element */
29683         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29684     },
29685
29686     applyConfig : function(c){
29687         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29688             var dh = Roo.DomHelper;
29689             if(c.titlebar !== false){
29690                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29691                 this.collapseBtn.on("click", this.collapse, this);
29692                 this.collapseBtn.enableDisplayMode();
29693
29694                 if(c.showPin === true || this.showPin){
29695                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29696                     this.stickBtn.enableDisplayMode();
29697                     this.stickBtn.on("click", this.expand, this);
29698                     this.stickBtn.hide();
29699                 }
29700             }
29701             /** This region's collapsed element
29702             * @type Roo.Element */
29703             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29704                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29705             ]}, true);
29706             if(c.floatable !== false){
29707                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29708                this.collapsedEl.on("click", this.collapseClick, this);
29709             }
29710
29711             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29712                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29713                    id: "message", unselectable: "on", style:{"float":"left"}});
29714                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29715              }
29716             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29717             this.expandBtn.on("click", this.expand, this);
29718         }
29719         if(this.collapseBtn){
29720             this.collapseBtn.setVisible(c.collapsible == true);
29721         }
29722         this.cmargins = c.cmargins || this.cmargins ||
29723                          (this.position == "west" || this.position == "east" ?
29724                              {top: 0, left: 2, right:2, bottom: 0} :
29725                              {top: 2, left: 0, right:0, bottom: 2});
29726         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29727         this.bottomTabs = c.tabPosition != "top";
29728         this.autoScroll = c.autoScroll || false;
29729         if(this.autoScroll){
29730             this.bodyEl.setStyle("overflow", "auto");
29731         }else{
29732             this.bodyEl.setStyle("overflow", "hidden");
29733         }
29734         //if(c.titlebar !== false){
29735             if((!c.titlebar && !c.title) || c.titlebar === false){
29736                 this.titleEl.hide();
29737             }else{
29738                 this.titleEl.show();
29739                 if(c.title){
29740                     this.titleTextEl.innerHTML = c.title;
29741                 }
29742             }
29743         //}
29744         this.duration = c.duration || .30;
29745         this.slideDuration = c.slideDuration || .45;
29746         this.config = c;
29747         if(c.collapsed){
29748             this.collapse(true);
29749         }
29750         if(c.hidden){
29751             this.hide();
29752         }
29753     },
29754     /**
29755      * Returns true if this region is currently visible.
29756      * @return {Boolean}
29757      */
29758     isVisible : function(){
29759         return this.visible;
29760     },
29761
29762     /**
29763      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29764      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29765      */
29766     setCollapsedTitle : function(title){
29767         title = title || "&#160;";
29768         if(this.collapsedTitleTextEl){
29769             this.collapsedTitleTextEl.innerHTML = title;
29770         }
29771     },
29772
29773     getBox : function(){
29774         var b;
29775         if(!this.collapsed){
29776             b = this.el.getBox(false, true);
29777         }else{
29778             b = this.collapsedEl.getBox(false, true);
29779         }
29780         return b;
29781     },
29782
29783     getMargins : function(){
29784         return this.collapsed ? this.cmargins : this.margins;
29785     },
29786
29787     highlight : function(){
29788         this.el.addClass("x-layout-panel-dragover");
29789     },
29790
29791     unhighlight : function(){
29792         this.el.removeClass("x-layout-panel-dragover");
29793     },
29794
29795     updateBox : function(box){
29796         this.box = box;
29797         if(!this.collapsed){
29798             this.el.dom.style.left = box.x + "px";
29799             this.el.dom.style.top = box.y + "px";
29800             this.updateBody(box.width, box.height);
29801         }else{
29802             this.collapsedEl.dom.style.left = box.x + "px";
29803             this.collapsedEl.dom.style.top = box.y + "px";
29804             this.collapsedEl.setSize(box.width, box.height);
29805         }
29806         if(this.tabs){
29807             this.tabs.autoSizeTabs();
29808         }
29809     },
29810
29811     updateBody : function(w, h){
29812         if(w !== null){
29813             this.el.setWidth(w);
29814             w -= this.el.getBorderWidth("rl");
29815             if(this.config.adjustments){
29816                 w += this.config.adjustments[0];
29817             }
29818         }
29819         if(h !== null){
29820             this.el.setHeight(h);
29821             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29822             h -= this.el.getBorderWidth("tb");
29823             if(this.config.adjustments){
29824                 h += this.config.adjustments[1];
29825             }
29826             this.bodyEl.setHeight(h);
29827             if(this.tabs){
29828                 h = this.tabs.syncHeight(h);
29829             }
29830         }
29831         if(this.panelSize){
29832             w = w !== null ? w : this.panelSize.width;
29833             h = h !== null ? h : this.panelSize.height;
29834         }
29835         if(this.activePanel){
29836             var el = this.activePanel.getEl();
29837             w = w !== null ? w : el.getWidth();
29838             h = h !== null ? h : el.getHeight();
29839             this.panelSize = {width: w, height: h};
29840             this.activePanel.setSize(w, h);
29841         }
29842         if(Roo.isIE && this.tabs){
29843             this.tabs.el.repaint();
29844         }
29845     },
29846
29847     /**
29848      * Returns the container element for this region.
29849      * @return {Roo.Element}
29850      */
29851     getEl : function(){
29852         return this.el;
29853     },
29854
29855     /**
29856      * Hides this region.
29857      */
29858     hide : function(){
29859         if(!this.collapsed){
29860             this.el.dom.style.left = "-2000px";
29861             this.el.hide();
29862         }else{
29863             this.collapsedEl.dom.style.left = "-2000px";
29864             this.collapsedEl.hide();
29865         }
29866         this.visible = false;
29867         this.fireEvent("visibilitychange", this, false);
29868     },
29869
29870     /**
29871      * Shows this region if it was previously hidden.
29872      */
29873     show : function(){
29874         if(!this.collapsed){
29875             this.el.show();
29876         }else{
29877             this.collapsedEl.show();
29878         }
29879         this.visible = true;
29880         this.fireEvent("visibilitychange", this, true);
29881     },
29882
29883     closeClicked : function(){
29884         if(this.activePanel){
29885             this.remove(this.activePanel);
29886         }
29887     },
29888
29889     collapseClick : function(e){
29890         if(this.isSlid){
29891            e.stopPropagation();
29892            this.slideIn();
29893         }else{
29894            e.stopPropagation();
29895            this.slideOut();
29896         }
29897     },
29898
29899     /**
29900      * Collapses this region.
29901      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29902      */
29903     collapse : function(skipAnim){
29904         if(this.collapsed) return;
29905         this.collapsed = true;
29906         if(this.split){
29907             this.split.el.hide();
29908         }
29909         if(this.config.animate && skipAnim !== true){
29910             this.fireEvent("invalidated", this);
29911             this.animateCollapse();
29912         }else{
29913             this.el.setLocation(-20000,-20000);
29914             this.el.hide();
29915             this.collapsedEl.show();
29916             this.fireEvent("collapsed", this);
29917             this.fireEvent("invalidated", this);
29918         }
29919     },
29920
29921     animateCollapse : function(){
29922         // overridden
29923     },
29924
29925     /**
29926      * Expands this region if it was previously collapsed.
29927      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29928      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29929      */
29930     expand : function(e, skipAnim){
29931         if(e) e.stopPropagation();
29932         if(!this.collapsed || this.el.hasActiveFx()) return;
29933         if(this.isSlid){
29934             this.afterSlideIn();
29935             skipAnim = true;
29936         }
29937         this.collapsed = false;
29938         if(this.config.animate && skipAnim !== true){
29939             this.animateExpand();
29940         }else{
29941             this.el.show();
29942             if(this.split){
29943                 this.split.el.show();
29944             }
29945             this.collapsedEl.setLocation(-2000,-2000);
29946             this.collapsedEl.hide();
29947             this.fireEvent("invalidated", this);
29948             this.fireEvent("expanded", this);
29949         }
29950     },
29951
29952     animateExpand : function(){
29953         // overridden
29954     },
29955
29956     initTabs : function(){
29957         this.bodyEl.setStyle("overflow", "hidden");
29958         var ts = new Roo.TabPanel(this.bodyEl.dom, {
29959             tabPosition: this.bottomTabs ? 'bottom' : 'top',
29960             disableTooltips: this.config.disableTabTips
29961         });
29962         if(this.config.hideTabs){
29963             ts.stripWrap.setDisplayed(false);
29964         }
29965         this.tabs = ts;
29966         ts.resizeTabs = this.config.resizeTabs === true;
29967         ts.minTabWidth = this.config.minTabWidth || 40;
29968         ts.maxTabWidth = this.config.maxTabWidth || 250;
29969         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29970         ts.monitorResize = false;
29971         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29972         ts.bodyEl.addClass('x-layout-tabs-body');
29973         this.panels.each(this.initPanelAsTab, this);
29974     },
29975
29976     initPanelAsTab : function(panel){
29977         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29978                     this.config.closeOnTab && panel.isClosable());
29979         if(panel.tabTip !== undefined){
29980             ti.setTooltip(panel.tabTip);
29981         }
29982         ti.on("activate", function(){
29983               this.setActivePanel(panel);
29984         }, this);
29985         if(this.config.closeOnTab){
29986             ti.on("beforeclose", function(t, e){
29987                 e.cancel = true;
29988                 this.remove(panel);
29989             }, this);
29990         }
29991         return ti;
29992     },
29993
29994     updatePanelTitle : function(panel, title){
29995         if(this.activePanel == panel){
29996             this.updateTitle(title);
29997         }
29998         if(this.tabs){
29999             var ti = this.tabs.getTab(panel.getEl().id);
30000             ti.setText(title);
30001             if(panel.tabTip !== undefined){
30002                 ti.setTooltip(panel.tabTip);
30003             }
30004         }
30005     },
30006
30007     updateTitle : function(title){
30008         if(this.titleTextEl && !this.config.title){
30009             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30010         }
30011     },
30012
30013     setActivePanel : function(panel){
30014         panel = this.getPanel(panel);
30015         if(this.activePanel && this.activePanel != panel){
30016             this.activePanel.setActiveState(false);
30017         }
30018         this.activePanel = panel;
30019         panel.setActiveState(true);
30020         if(this.panelSize){
30021             panel.setSize(this.panelSize.width, this.panelSize.height);
30022         }
30023         if(this.closeBtn){
30024             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30025         }
30026         this.updateTitle(panel.getTitle());
30027         if(this.tabs){
30028             this.fireEvent("invalidated", this);
30029         }
30030         this.fireEvent("panelactivated", this, panel);
30031     },
30032
30033     /**
30034      * Shows the specified panel.
30035      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30036      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30037      */
30038     showPanel : function(panel){
30039         if(panel = this.getPanel(panel)){
30040             if(this.tabs){
30041                 var tab = this.tabs.getTab(panel.getEl().id);
30042                 if(tab.isHidden()){
30043                     this.tabs.unhideTab(tab.id);
30044                 }
30045                 tab.activate();
30046             }else{
30047                 this.setActivePanel(panel);
30048             }
30049         }
30050         return panel;
30051     },
30052
30053     /**
30054      * Get the active panel for this region.
30055      * @return {Roo.ContentPanel} The active panel or null
30056      */
30057     getActivePanel : function(){
30058         return this.activePanel;
30059     },
30060
30061     validateVisibility : function(){
30062         if(this.panels.getCount() < 1){
30063             this.updateTitle("&#160;");
30064             this.closeBtn.hide();
30065             this.hide();
30066         }else{
30067             if(!this.isVisible()){
30068                 this.show();
30069             }
30070         }
30071     },
30072
30073     /**
30074      * Adds the passed ContentPanel(s) to this region.
30075      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30076      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30077      */
30078     add : function(panel){
30079         if(arguments.length > 1){
30080             for(var i = 0, len = arguments.length; i < len; i++) {
30081                 this.add(arguments[i]);
30082             }
30083             return null;
30084         }
30085         if(this.hasPanel(panel)){
30086             this.showPanel(panel);
30087             return panel;
30088         }
30089         panel.setRegion(this);
30090         this.panels.add(panel);
30091         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30092             this.bodyEl.dom.appendChild(panel.getEl().dom);
30093             if(panel.background !== true){
30094                 this.setActivePanel(panel);
30095             }
30096             this.fireEvent("paneladded", this, panel);
30097             return panel;
30098         }
30099         if(!this.tabs){
30100             this.initTabs();
30101         }else{
30102             this.initPanelAsTab(panel);
30103         }
30104         if(panel.background !== true){
30105             this.tabs.activate(panel.getEl().id);
30106         }
30107         this.fireEvent("paneladded", this, panel);
30108         return panel;
30109     },
30110
30111     /**
30112      * Hides the tab for the specified panel.
30113      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30114      */
30115     hidePanel : function(panel){
30116         if(this.tabs && (panel = this.getPanel(panel))){
30117             this.tabs.hideTab(panel.getEl().id);
30118         }
30119     },
30120
30121     /**
30122      * Unhides the tab for a previously hidden panel.
30123      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30124      */
30125     unhidePanel : function(panel){
30126         if(this.tabs && (panel = this.getPanel(panel))){
30127             this.tabs.unhideTab(panel.getEl().id);
30128         }
30129     },
30130
30131     clearPanels : function(){
30132         while(this.panels.getCount() > 0){
30133              this.remove(this.panels.first());
30134         }
30135     },
30136
30137     /**
30138      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30139      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30140      * @param {Boolean} preservePanel Overrides the config preservePanel option
30141      * @return {Roo.ContentPanel} The panel that was removed
30142      */
30143     remove : function(panel, preservePanel){
30144         panel = this.getPanel(panel);
30145         if(!panel){
30146             return null;
30147         }
30148         var e = {};
30149         this.fireEvent("beforeremove", this, panel, e);
30150         if(e.cancel === true){
30151             return null;
30152         }
30153         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30154         var panelId = panel.getId();
30155         this.panels.removeKey(panelId);
30156         if(preservePanel){
30157             document.body.appendChild(panel.getEl().dom);
30158         }
30159         if(this.tabs){
30160             this.tabs.removeTab(panel.getEl().id);
30161         }else if (!preservePanel){
30162             this.bodyEl.dom.removeChild(panel.getEl().dom);
30163         }
30164         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30165             var p = this.panels.first();
30166             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30167             tempEl.appendChild(p.getEl().dom);
30168             this.bodyEl.update("");
30169             this.bodyEl.dom.appendChild(p.getEl().dom);
30170             tempEl = null;
30171             this.updateTitle(p.getTitle());
30172             this.tabs = null;
30173             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30174             this.setActivePanel(p);
30175         }
30176         panel.setRegion(null);
30177         if(this.activePanel == panel){
30178             this.activePanel = null;
30179         }
30180         if(this.config.autoDestroy !== false && preservePanel !== true){
30181             try{panel.destroy();}catch(e){}
30182         }
30183         this.fireEvent("panelremoved", this, panel);
30184         return panel;
30185     },
30186
30187     /**
30188      * Returns the TabPanel component used by this region
30189      * @return {Roo.TabPanel}
30190      */
30191     getTabs : function(){
30192         return this.tabs;
30193     },
30194
30195     createTool : function(parentEl, className){
30196         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30197             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30198         btn.addClassOnOver("x-layout-tools-button-over");
30199         return btn;
30200     }
30201 });/*
30202  * Based on:
30203  * Ext JS Library 1.1.1
30204  * Copyright(c) 2006-2007, Ext JS, LLC.
30205  *
30206  * Originally Released Under LGPL - original licence link has changed is not relivant.
30207  *
30208  * Fork - LGPL
30209  * <script type="text/javascript">
30210  */
30211  
30212
30213
30214 /**
30215  * @class Roo.SplitLayoutRegion
30216  * @extends Roo.LayoutRegion
30217  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30218  */
30219 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30220     this.cursor = cursor;
30221     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30222 };
30223
30224 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30225     splitTip : "Drag to resize.",
30226     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30227     useSplitTips : false,
30228
30229     applyConfig : function(config){
30230         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30231         if(config.split){
30232             if(!this.split){
30233                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30234                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30235                 /** The SplitBar for this region 
30236                 * @type Roo.SplitBar */
30237                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30238                 this.split.on("moved", this.onSplitMove, this);
30239                 this.split.useShim = config.useShim === true;
30240                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30241                 if(this.useSplitTips){
30242                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30243                 }
30244                 if(config.collapsible){
30245                     this.split.el.on("dblclick", this.collapse,  this);
30246                 }
30247             }
30248             if(typeof config.minSize != "undefined"){
30249                 this.split.minSize = config.minSize;
30250             }
30251             if(typeof config.maxSize != "undefined"){
30252                 this.split.maxSize = config.maxSize;
30253             }
30254             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30255                 this.hideSplitter();
30256             }
30257         }
30258     },
30259
30260     getHMaxSize : function(){
30261          var cmax = this.config.maxSize || 10000;
30262          var center = this.mgr.getRegion("center");
30263          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30264     },
30265
30266     getVMaxSize : function(){
30267          var cmax = this.config.maxSize || 10000;
30268          var center = this.mgr.getRegion("center");
30269          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30270     },
30271
30272     onSplitMove : function(split, newSize){
30273         this.fireEvent("resized", this, newSize);
30274     },
30275     
30276     /** 
30277      * Returns the {@link Roo.SplitBar} for this region.
30278      * @return {Roo.SplitBar}
30279      */
30280     getSplitBar : function(){
30281         return this.split;
30282     },
30283     
30284     hide : function(){
30285         this.hideSplitter();
30286         Roo.SplitLayoutRegion.superclass.hide.call(this);
30287     },
30288
30289     hideSplitter : function(){
30290         if(this.split){
30291             this.split.el.setLocation(-2000,-2000);
30292             this.split.el.hide();
30293         }
30294     },
30295
30296     show : function(){
30297         if(this.split){
30298             this.split.el.show();
30299         }
30300         Roo.SplitLayoutRegion.superclass.show.call(this);
30301     },
30302     
30303     beforeSlide: function(){
30304         if(Roo.isGecko){// firefox overflow auto bug workaround
30305             this.bodyEl.clip();
30306             if(this.tabs) this.tabs.bodyEl.clip();
30307             if(this.activePanel){
30308                 this.activePanel.getEl().clip();
30309                 
30310                 if(this.activePanel.beforeSlide){
30311                     this.activePanel.beforeSlide();
30312                 }
30313             }
30314         }
30315     },
30316     
30317     afterSlide : function(){
30318         if(Roo.isGecko){// firefox overflow auto bug workaround
30319             this.bodyEl.unclip();
30320             if(this.tabs) this.tabs.bodyEl.unclip();
30321             if(this.activePanel){
30322                 this.activePanel.getEl().unclip();
30323                 if(this.activePanel.afterSlide){
30324                     this.activePanel.afterSlide();
30325                 }
30326             }
30327         }
30328     },
30329
30330     initAutoHide : function(){
30331         if(this.autoHide !== false){
30332             if(!this.autoHideHd){
30333                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30334                 this.autoHideHd = {
30335                     "mouseout": function(e){
30336                         if(!e.within(this.el, true)){
30337                             st.delay(500);
30338                         }
30339                     },
30340                     "mouseover" : function(e){
30341                         st.cancel();
30342                     },
30343                     scope : this
30344                 };
30345             }
30346             this.el.on(this.autoHideHd);
30347         }
30348     },
30349
30350     clearAutoHide : function(){
30351         if(this.autoHide !== false){
30352             this.el.un("mouseout", this.autoHideHd.mouseout);
30353             this.el.un("mouseover", this.autoHideHd.mouseover);
30354         }
30355     },
30356
30357     clearMonitor : function(){
30358         Roo.get(document).un("click", this.slideInIf, this);
30359     },
30360
30361     // these names are backwards but not changed for compat
30362     slideOut : function(){
30363         if(this.isSlid || this.el.hasActiveFx()){
30364             return;
30365         }
30366         this.isSlid = true;
30367         if(this.collapseBtn){
30368             this.collapseBtn.hide();
30369         }
30370         this.closeBtnState = this.closeBtn.getStyle('display');
30371         this.closeBtn.hide();
30372         if(this.stickBtn){
30373             this.stickBtn.show();
30374         }
30375         this.el.show();
30376         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30377         this.beforeSlide();
30378         this.el.setStyle("z-index", 10001);
30379         this.el.slideIn(this.getSlideAnchor(), {
30380             callback: function(){
30381                 this.afterSlide();
30382                 this.initAutoHide();
30383                 Roo.get(document).on("click", this.slideInIf, this);
30384                 this.fireEvent("slideshow", this);
30385             },
30386             scope: this,
30387             block: true
30388         });
30389     },
30390
30391     afterSlideIn : function(){
30392         this.clearAutoHide();
30393         this.isSlid = false;
30394         this.clearMonitor();
30395         this.el.setStyle("z-index", "");
30396         if(this.collapseBtn){
30397             this.collapseBtn.show();
30398         }
30399         this.closeBtn.setStyle('display', this.closeBtnState);
30400         if(this.stickBtn){
30401             this.stickBtn.hide();
30402         }
30403         this.fireEvent("slidehide", this);
30404     },
30405
30406     slideIn : function(cb){
30407         if(!this.isSlid || this.el.hasActiveFx()){
30408             Roo.callback(cb);
30409             return;
30410         }
30411         this.isSlid = false;
30412         this.beforeSlide();
30413         this.el.slideOut(this.getSlideAnchor(), {
30414             callback: function(){
30415                 this.el.setLeftTop(-10000, -10000);
30416                 this.afterSlide();
30417                 this.afterSlideIn();
30418                 Roo.callback(cb);
30419             },
30420             scope: this,
30421             block: true
30422         });
30423     },
30424     
30425     slideInIf : function(e){
30426         if(!e.within(this.el)){
30427             this.slideIn();
30428         }
30429     },
30430
30431     animateCollapse : function(){
30432         this.beforeSlide();
30433         this.el.setStyle("z-index", 20000);
30434         var anchor = this.getSlideAnchor();
30435         this.el.slideOut(anchor, {
30436             callback : function(){
30437                 this.el.setStyle("z-index", "");
30438                 this.collapsedEl.slideIn(anchor, {duration:.3});
30439                 this.afterSlide();
30440                 this.el.setLocation(-10000,-10000);
30441                 this.el.hide();
30442                 this.fireEvent("collapsed", this);
30443             },
30444             scope: this,
30445             block: true
30446         });
30447     },
30448
30449     animateExpand : function(){
30450         this.beforeSlide();
30451         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30452         this.el.setStyle("z-index", 20000);
30453         this.collapsedEl.hide({
30454             duration:.1
30455         });
30456         this.el.slideIn(this.getSlideAnchor(), {
30457             callback : function(){
30458                 this.el.setStyle("z-index", "");
30459                 this.afterSlide();
30460                 if(this.split){
30461                     this.split.el.show();
30462                 }
30463                 this.fireEvent("invalidated", this);
30464                 this.fireEvent("expanded", this);
30465             },
30466             scope: this,
30467             block: true
30468         });
30469     },
30470
30471     anchors : {
30472         "west" : "left",
30473         "east" : "right",
30474         "north" : "top",
30475         "south" : "bottom"
30476     },
30477
30478     sanchors : {
30479         "west" : "l",
30480         "east" : "r",
30481         "north" : "t",
30482         "south" : "b"
30483     },
30484
30485     canchors : {
30486         "west" : "tl-tr",
30487         "east" : "tr-tl",
30488         "north" : "tl-bl",
30489         "south" : "bl-tl"
30490     },
30491
30492     getAnchor : function(){
30493         return this.anchors[this.position];
30494     },
30495
30496     getCollapseAnchor : function(){
30497         return this.canchors[this.position];
30498     },
30499
30500     getSlideAnchor : function(){
30501         return this.sanchors[this.position];
30502     },
30503
30504     getAlignAdj : function(){
30505         var cm = this.cmargins;
30506         switch(this.position){
30507             case "west":
30508                 return [0, 0];
30509             break;
30510             case "east":
30511                 return [0, 0];
30512             break;
30513             case "north":
30514                 return [0, 0];
30515             break;
30516             case "south":
30517                 return [0, 0];
30518             break;
30519         }
30520     },
30521
30522     getExpandAdj : function(){
30523         var c = this.collapsedEl, cm = this.cmargins;
30524         switch(this.position){
30525             case "west":
30526                 return [-(cm.right+c.getWidth()+cm.left), 0];
30527             break;
30528             case "east":
30529                 return [cm.right+c.getWidth()+cm.left, 0];
30530             break;
30531             case "north":
30532                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30533             break;
30534             case "south":
30535                 return [0, cm.top+cm.bottom+c.getHeight()];
30536             break;
30537         }
30538     }
30539 });/*
30540  * Based on:
30541  * Ext JS Library 1.1.1
30542  * Copyright(c) 2006-2007, Ext JS, LLC.
30543  *
30544  * Originally Released Under LGPL - original licence link has changed is not relivant.
30545  *
30546  * Fork - LGPL
30547  * <script type="text/javascript">
30548  */
30549 /*
30550  * These classes are private internal classes
30551  */
30552 Roo.CenterLayoutRegion = function(mgr, config){
30553     Roo.LayoutRegion.call(this, mgr, config, "center");
30554     this.visible = true;
30555     this.minWidth = config.minWidth || 20;
30556     this.minHeight = config.minHeight || 20;
30557 };
30558
30559 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30560     hide : function(){
30561         // center panel can't be hidden
30562     },
30563     
30564     show : function(){
30565         // center panel can't be hidden
30566     },
30567     
30568     getMinWidth: function(){
30569         return this.minWidth;
30570     },
30571     
30572     getMinHeight: function(){
30573         return this.minHeight;
30574     }
30575 });
30576
30577
30578 Roo.NorthLayoutRegion = function(mgr, config){
30579     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30580     if(this.split){
30581         this.split.placement = Roo.SplitBar.TOP;
30582         this.split.orientation = Roo.SplitBar.VERTICAL;
30583         this.split.el.addClass("x-layout-split-v");
30584     }
30585     var size = config.initialSize || config.height;
30586     if(typeof size != "undefined"){
30587         this.el.setHeight(size);
30588     }
30589 };
30590 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30591     orientation: Roo.SplitBar.VERTICAL,
30592     getBox : function(){
30593         if(this.collapsed){
30594             return this.collapsedEl.getBox();
30595         }
30596         var box = this.el.getBox();
30597         if(this.split){
30598             box.height += this.split.el.getHeight();
30599         }
30600         return box;
30601     },
30602     
30603     updateBox : function(box){
30604         if(this.split && !this.collapsed){
30605             box.height -= this.split.el.getHeight();
30606             this.split.el.setLeft(box.x);
30607             this.split.el.setTop(box.y+box.height);
30608             this.split.el.setWidth(box.width);
30609         }
30610         if(this.collapsed){
30611             this.updateBody(box.width, null);
30612         }
30613         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30614     }
30615 });
30616
30617 Roo.SouthLayoutRegion = function(mgr, config){
30618     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30619     if(this.split){
30620         this.split.placement = Roo.SplitBar.BOTTOM;
30621         this.split.orientation = Roo.SplitBar.VERTICAL;
30622         this.split.el.addClass("x-layout-split-v");
30623     }
30624     var size = config.initialSize || config.height;
30625     if(typeof size != "undefined"){
30626         this.el.setHeight(size);
30627     }
30628 };
30629 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30630     orientation: Roo.SplitBar.VERTICAL,
30631     getBox : function(){
30632         if(this.collapsed){
30633             return this.collapsedEl.getBox();
30634         }
30635         var box = this.el.getBox();
30636         if(this.split){
30637             var sh = this.split.el.getHeight();
30638             box.height += sh;
30639             box.y -= sh;
30640         }
30641         return box;
30642     },
30643     
30644     updateBox : function(box){
30645         if(this.split && !this.collapsed){
30646             var sh = this.split.el.getHeight();
30647             box.height -= sh;
30648             box.y += sh;
30649             this.split.el.setLeft(box.x);
30650             this.split.el.setTop(box.y-sh);
30651             this.split.el.setWidth(box.width);
30652         }
30653         if(this.collapsed){
30654             this.updateBody(box.width, null);
30655         }
30656         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30657     }
30658 });
30659
30660 Roo.EastLayoutRegion = function(mgr, config){
30661     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30662     if(this.split){
30663         this.split.placement = Roo.SplitBar.RIGHT;
30664         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30665         this.split.el.addClass("x-layout-split-h");
30666     }
30667     var size = config.initialSize || config.width;
30668     if(typeof size != "undefined"){
30669         this.el.setWidth(size);
30670     }
30671 };
30672 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30673     orientation: Roo.SplitBar.HORIZONTAL,
30674     getBox : function(){
30675         if(this.collapsed){
30676             return this.collapsedEl.getBox();
30677         }
30678         var box = this.el.getBox();
30679         if(this.split){
30680             var sw = this.split.el.getWidth();
30681             box.width += sw;
30682             box.x -= sw;
30683         }
30684         return box;
30685     },
30686
30687     updateBox : function(box){
30688         if(this.split && !this.collapsed){
30689             var sw = this.split.el.getWidth();
30690             box.width -= sw;
30691             this.split.el.setLeft(box.x);
30692             this.split.el.setTop(box.y);
30693             this.split.el.setHeight(box.height);
30694             box.x += sw;
30695         }
30696         if(this.collapsed){
30697             this.updateBody(null, box.height);
30698         }
30699         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30700     }
30701 });
30702
30703 Roo.WestLayoutRegion = function(mgr, config){
30704     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30705     if(this.split){
30706         this.split.placement = Roo.SplitBar.LEFT;
30707         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30708         this.split.el.addClass("x-layout-split-h");
30709     }
30710     var size = config.initialSize || config.width;
30711     if(typeof size != "undefined"){
30712         this.el.setWidth(size);
30713     }
30714 };
30715 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30716     orientation: Roo.SplitBar.HORIZONTAL,
30717     getBox : function(){
30718         if(this.collapsed){
30719             return this.collapsedEl.getBox();
30720         }
30721         var box = this.el.getBox();
30722         if(this.split){
30723             box.width += this.split.el.getWidth();
30724         }
30725         return box;
30726     },
30727     
30728     updateBox : function(box){
30729         if(this.split && !this.collapsed){
30730             var sw = this.split.el.getWidth();
30731             box.width -= sw;
30732             this.split.el.setLeft(box.x+box.width);
30733             this.split.el.setTop(box.y);
30734             this.split.el.setHeight(box.height);
30735         }
30736         if(this.collapsed){
30737             this.updateBody(null, box.height);
30738         }
30739         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30740     }
30741 });
30742 /*
30743  * Based on:
30744  * Ext JS Library 1.1.1
30745  * Copyright(c) 2006-2007, Ext JS, LLC.
30746  *
30747  * Originally Released Under LGPL - original licence link has changed is not relivant.
30748  *
30749  * Fork - LGPL
30750  * <script type="text/javascript">
30751  */
30752  
30753  
30754 /*
30755  * Private internal class for reading and applying state
30756  */
30757 Roo.LayoutStateManager = function(layout){
30758      // default empty state
30759      this.state = {
30760         north: {},
30761         south: {},
30762         east: {},
30763         west: {}       
30764     };
30765 };
30766
30767 Roo.LayoutStateManager.prototype = {
30768     init : function(layout, provider){
30769         this.provider = provider;
30770         var state = provider.get(layout.id+"-layout-state");
30771         if(state){
30772             var wasUpdating = layout.isUpdating();
30773             if(!wasUpdating){
30774                 layout.beginUpdate();
30775             }
30776             for(var key in state){
30777                 if(typeof state[key] != "function"){
30778                     var rstate = state[key];
30779                     var r = layout.getRegion(key);
30780                     if(r && rstate){
30781                         if(rstate.size){
30782                             r.resizeTo(rstate.size);
30783                         }
30784                         if(rstate.collapsed == true){
30785                             r.collapse(true);
30786                         }else{
30787                             r.expand(null, true);
30788                         }
30789                     }
30790                 }
30791             }
30792             if(!wasUpdating){
30793                 layout.endUpdate();
30794             }
30795             this.state = state; 
30796         }
30797         this.layout = layout;
30798         layout.on("regionresized", this.onRegionResized, this);
30799         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30800         layout.on("regionexpanded", this.onRegionExpanded, this);
30801     },
30802     
30803     storeState : function(){
30804         this.provider.set(this.layout.id+"-layout-state", this.state);
30805     },
30806     
30807     onRegionResized : function(region, newSize){
30808         this.state[region.getPosition()].size = newSize;
30809         this.storeState();
30810     },
30811     
30812     onRegionCollapsed : function(region){
30813         this.state[region.getPosition()].collapsed = true;
30814         this.storeState();
30815     },
30816     
30817     onRegionExpanded : function(region){
30818         this.state[region.getPosition()].collapsed = false;
30819         this.storeState();
30820     }
30821 };/*
30822  * Based on:
30823  * Ext JS Library 1.1.1
30824  * Copyright(c) 2006-2007, Ext JS, LLC.
30825  *
30826  * Originally Released Under LGPL - original licence link has changed is not relivant.
30827  *
30828  * Fork - LGPL
30829  * <script type="text/javascript">
30830  */
30831 /**
30832  * @class Roo.ContentPanel
30833  * @extends Roo.util.Observable
30834  * A basic ContentPanel element.
30835  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30836  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30837  * @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
30838  * @cfg {Boolean} closable True if the panel can be closed/removed
30839  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30840  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30841  * @cfg {Toolbar} toolbar A toolbar for this panel
30842  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30843  * @cfg {String} title The title for this panel
30844  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30845  * @cfg {String} url Calls {@link #setUrl} with this value
30846  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30847  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30848  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30849  * @constructor
30850  * Create a new ContentPanel.
30851  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30852  * @param {String/Object} config A string to set only the title or a config object
30853  * @param {String} content (optional) Set the HTML content for this panel
30854  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30855  */
30856 Roo.ContentPanel = function(el, config, content){
30857     
30858      
30859     /*
30860     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30861         config = el;
30862         el = Roo.id();
30863     }
30864     if (config && config.parentLayout) { 
30865         el = config.parentLayout.el.createChild(); 
30866     }
30867     */
30868     if(el.autoCreate){ // xtype is available if this is called from factory
30869         config = el;
30870         el = Roo.id();
30871     }
30872     this.el = Roo.get(el);
30873     if(!this.el && config && config.autoCreate){
30874         if(typeof config.autoCreate == "object"){
30875             if(!config.autoCreate.id){
30876                 config.autoCreate.id = config.id||el;
30877             }
30878             this.el = Roo.DomHelper.append(document.body,
30879                         config.autoCreate, true);
30880         }else{
30881             this.el = Roo.DomHelper.append(document.body,
30882                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30883         }
30884     }
30885     this.closable = false;
30886     this.loaded = false;
30887     this.active = false;
30888     if(typeof config == "string"){
30889         this.title = config;
30890     }else{
30891         Roo.apply(this, config);
30892     }
30893     
30894     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30895         this.wrapEl = this.el.wrap();    
30896         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30897         
30898     }
30899     
30900     
30901     
30902     if(this.resizeEl){
30903         this.resizeEl = Roo.get(this.resizeEl, true);
30904     }else{
30905         this.resizeEl = this.el;
30906     }
30907     this.addEvents({
30908         /**
30909          * @event activate
30910          * Fires when this panel is activated. 
30911          * @param {Roo.ContentPanel} this
30912          */
30913         "activate" : true,
30914         /**
30915          * @event deactivate
30916          * Fires when this panel is activated. 
30917          * @param {Roo.ContentPanel} this
30918          */
30919         "deactivate" : true,
30920
30921         /**
30922          * @event resize
30923          * Fires when this panel is resized if fitToFrame is true.
30924          * @param {Roo.ContentPanel} this
30925          * @param {Number} width The width after any component adjustments
30926          * @param {Number} height The height after any component adjustments
30927          */
30928         "resize" : true
30929     });
30930     if(this.autoScroll){
30931         this.resizeEl.setStyle("overflow", "auto");
30932     } else {
30933         // fix randome scrolling
30934         this.el.on('scroll', function() {
30935             this.scrollTo('top',0); 
30936         });
30937     }
30938     content = content || this.content;
30939     if(content){
30940         this.setContent(content);
30941     }
30942     if(config && config.url){
30943         this.setUrl(this.url, this.params, this.loadOnce);
30944     }
30945     
30946     
30947     
30948     Roo.ContentPanel.superclass.constructor.call(this);
30949 };
30950
30951 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30952     tabTip:'',
30953     setRegion : function(region){
30954         this.region = region;
30955         if(region){
30956            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30957         }else{
30958            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30959         } 
30960     },
30961     
30962     /**
30963      * Returns the toolbar for this Panel if one was configured. 
30964      * @return {Roo.Toolbar} 
30965      */
30966     getToolbar : function(){
30967         return this.toolbar;
30968     },
30969     
30970     setActiveState : function(active){
30971         this.active = active;
30972         if(!active){
30973             this.fireEvent("deactivate", this);
30974         }else{
30975             this.fireEvent("activate", this);
30976         }
30977     },
30978     /**
30979      * Updates this panel's element
30980      * @param {String} content The new content
30981      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30982     */
30983     setContent : function(content, loadScripts){
30984         this.el.update(content, loadScripts);
30985     },
30986
30987     ignoreResize : function(w, h){
30988         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30989             return true;
30990         }else{
30991             this.lastSize = {width: w, height: h};
30992             return false;
30993         }
30994     },
30995     /**
30996      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30997      * @return {Roo.UpdateManager} The UpdateManager
30998      */
30999     getUpdateManager : function(){
31000         return this.el.getUpdateManager();
31001     },
31002      /**
31003      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31004      * @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:
31005 <pre><code>
31006 panel.load({
31007     url: "your-url.php",
31008     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31009     callback: yourFunction,
31010     scope: yourObject, //(optional scope)
31011     discardUrl: false,
31012     nocache: false,
31013     text: "Loading...",
31014     timeout: 30,
31015     scripts: false
31016 });
31017 </code></pre>
31018      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31019      * 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.
31020      * @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}
31021      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31022      * @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.
31023      * @return {Roo.ContentPanel} this
31024      */
31025     load : function(){
31026         var um = this.el.getUpdateManager();
31027         um.update.apply(um, arguments);
31028         return this;
31029     },
31030
31031
31032     /**
31033      * 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.
31034      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31035      * @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)
31036      * @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)
31037      * @return {Roo.UpdateManager} The UpdateManager
31038      */
31039     setUrl : function(url, params, loadOnce){
31040         if(this.refreshDelegate){
31041             this.removeListener("activate", this.refreshDelegate);
31042         }
31043         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31044         this.on("activate", this.refreshDelegate);
31045         return this.el.getUpdateManager();
31046     },
31047     
31048     _handleRefresh : function(url, params, loadOnce){
31049         if(!loadOnce || !this.loaded){
31050             var updater = this.el.getUpdateManager();
31051             updater.update(url, params, this._setLoaded.createDelegate(this));
31052         }
31053     },
31054     
31055     _setLoaded : function(){
31056         this.loaded = true;
31057     }, 
31058     
31059     /**
31060      * Returns this panel's id
31061      * @return {String} 
31062      */
31063     getId : function(){
31064         return this.el.id;
31065     },
31066     
31067     /** 
31068      * Returns this panel's element - used by regiosn to add.
31069      * @return {Roo.Element} 
31070      */
31071     getEl : function(){
31072         return this.wrapEl || this.el;
31073     },
31074     
31075     adjustForComponents : function(width, height){
31076         if(this.resizeEl != this.el){
31077             width -= this.el.getFrameWidth('lr');
31078             height -= this.el.getFrameWidth('tb');
31079         }
31080         if(this.toolbar){
31081             var te = this.toolbar.getEl();
31082             height -= te.getHeight();
31083             te.setWidth(width);
31084         }
31085         if(this.adjustments){
31086             width += this.adjustments[0];
31087             height += this.adjustments[1];
31088         }
31089         return {"width": width, "height": height};
31090     },
31091     
31092     setSize : function(width, height){
31093         if(this.fitToFrame && !this.ignoreResize(width, height)){
31094             if(this.fitContainer && this.resizeEl != this.el){
31095                 this.el.setSize(width, height);
31096             }
31097             var size = this.adjustForComponents(width, height);
31098             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31099             this.fireEvent('resize', this, size.width, size.height);
31100         }
31101     },
31102     
31103     /**
31104      * Returns this panel's title
31105      * @return {String} 
31106      */
31107     getTitle : function(){
31108         return this.title;
31109     },
31110     
31111     /**
31112      * Set this panel's title
31113      * @param {String} title
31114      */
31115     setTitle : function(title){
31116         this.title = title;
31117         if(this.region){
31118             this.region.updatePanelTitle(this, title);
31119         }
31120     },
31121     
31122     /**
31123      * Returns true is this panel was configured to be closable
31124      * @return {Boolean} 
31125      */
31126     isClosable : function(){
31127         return this.closable;
31128     },
31129     
31130     beforeSlide : function(){
31131         this.el.clip();
31132         this.resizeEl.clip();
31133     },
31134     
31135     afterSlide : function(){
31136         this.el.unclip();
31137         this.resizeEl.unclip();
31138     },
31139     
31140     /**
31141      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31142      *   Will fail silently if the {@link #setUrl} method has not been called.
31143      *   This does not activate the panel, just updates its content.
31144      */
31145     refresh : function(){
31146         if(this.refreshDelegate){
31147            this.loaded = false;
31148            this.refreshDelegate();
31149         }
31150     },
31151     
31152     /**
31153      * Destroys this panel
31154      */
31155     destroy : function(){
31156         this.el.removeAllListeners();
31157         var tempEl = document.createElement("span");
31158         tempEl.appendChild(this.el.dom);
31159         tempEl.innerHTML = "";
31160         this.el.remove();
31161         this.el = null;
31162     },
31163     
31164       /**
31165      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31166      * <pre><code>
31167
31168 layout.addxtype({
31169        xtype : 'Form',
31170        items: [ .... ]
31171    }
31172 );
31173
31174 </code></pre>
31175      * @param {Object} cfg Xtype definition of item to add.
31176      */
31177     
31178     addxtype : function(cfg) {
31179         // add form..
31180         if (cfg.xtype.match(/^Form$/)) {
31181             var el = this.el.createChild();
31182
31183             this.form = new  Roo.form.Form(cfg);
31184             
31185             
31186             if ( this.form.allItems.length) this.form.render(el.dom);
31187             return this.form;
31188         }
31189         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31190             // views..
31191             cfg.el = this.el.appendChild(document.createElement("div"));
31192             // factory?
31193             var ret = new Roo[cfg.xtype](cfg);
31194             ret.render(false, ''); // render blank..
31195             return ret;
31196             
31197         }
31198         return false;
31199         
31200     }
31201 });
31202
31203 /**
31204  * @class Roo.GridPanel
31205  * @extends Roo.ContentPanel
31206  * @constructor
31207  * Create a new GridPanel.
31208  * @param {Roo.grid.Grid} grid The grid for this panel
31209  * @param {String/Object} config A string to set only the panel's title, or a config object
31210  */
31211 Roo.GridPanel = function(grid, config){
31212     
31213   
31214     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31215         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31216         
31217     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31218     
31219     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31220     
31221     if(this.toolbar){
31222         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31223     }
31224     // xtype created footer. - not sure if will work as we normally have to render first..
31225     if (this.footer && !this.footer.el && this.footer.xtype) {
31226         
31227         this.footer.container = this.grid.getView().getFooterPanel(true);
31228         this.footer.dataSource = this.grid.dataSource;
31229         this.footer = Roo.factory(this.footer, Roo);
31230         
31231     }
31232     
31233     grid.monitorWindowResize = false; // turn off autosizing
31234     grid.autoHeight = false;
31235     grid.autoWidth = false;
31236     this.grid = grid;
31237     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31238 };
31239
31240 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31241     getId : function(){
31242         return this.grid.id;
31243     },
31244     
31245     /**
31246      * Returns the grid for this panel
31247      * @return {Roo.grid.Grid} 
31248      */
31249     getGrid : function(){
31250         return this.grid;    
31251     },
31252     
31253     setSize : function(width, height){
31254         if(!this.ignoreResize(width, height)){
31255             var grid = this.grid;
31256             var size = this.adjustForComponents(width, height);
31257             grid.getGridEl().setSize(size.width, size.height);
31258             grid.autoSize();
31259         }
31260     },
31261     
31262     beforeSlide : function(){
31263         this.grid.getView().scroller.clip();
31264     },
31265     
31266     afterSlide : function(){
31267         this.grid.getView().scroller.unclip();
31268     },
31269     
31270     destroy : function(){
31271         this.grid.destroy();
31272         delete this.grid;
31273         Roo.GridPanel.superclass.destroy.call(this); 
31274     }
31275 });
31276
31277
31278 /**
31279  * @class Roo.NestedLayoutPanel
31280  * @extends Roo.ContentPanel
31281  * @constructor
31282  * Create a new NestedLayoutPanel.
31283  * 
31284  * 
31285  * @param {Roo.BorderLayout} layout The layout for this panel
31286  * @param {String/Object} config A string to set only the title or a config object
31287  */
31288 Roo.NestedLayoutPanel = function(layout, config)
31289 {
31290     // construct with only one argument..
31291     /* FIXME - implement nicer consturctors
31292     if (layout.layout) {
31293         config = layout;
31294         layout = config.layout;
31295         delete config.layout;
31296     }
31297     if (layout.xtype && !layout.getEl) {
31298         // then layout needs constructing..
31299         layout = Roo.factory(layout, Roo);
31300     }
31301     */
31302     
31303     
31304     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31305     
31306     layout.monitorWindowResize = false; // turn off autosizing
31307     this.layout = layout;
31308     this.layout.getEl().addClass("x-layout-nested-layout");
31309     
31310     
31311     
31312     
31313 };
31314
31315 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31316
31317     setSize : function(width, height){
31318         if(!this.ignoreResize(width, height)){
31319             var size = this.adjustForComponents(width, height);
31320             var el = this.layout.getEl();
31321             el.setSize(size.width, size.height);
31322             var touch = el.dom.offsetWidth;
31323             this.layout.layout();
31324             // ie requires a double layout on the first pass
31325             if(Roo.isIE && !this.initialized){
31326                 this.initialized = true;
31327                 this.layout.layout();
31328             }
31329         }
31330     },
31331     
31332     // activate all subpanels if not currently active..
31333     
31334     setActiveState : function(active){
31335         this.active = active;
31336         if(!active){
31337             this.fireEvent("deactivate", this);
31338             return;
31339         }
31340         
31341         this.fireEvent("activate", this);
31342         // not sure if this should happen before or after..
31343         if (!this.layout) {
31344             return; // should not happen..
31345         }
31346         var reg = false;
31347         for (var r in this.layout.regions) {
31348             reg = this.layout.getRegion(r);
31349             if (reg.getActivePanel()) {
31350                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31351                 reg.setActivePanel(reg.getActivePanel());
31352                 continue;
31353             }
31354             if (!reg.panels.length) {
31355                 continue;
31356             }
31357             reg.showPanel(reg.getPanel(0));
31358         }
31359         
31360         
31361         
31362         
31363     },
31364     
31365     /**
31366      * Returns the nested BorderLayout for this panel
31367      * @return {Roo.BorderLayout} 
31368      */
31369     getLayout : function(){
31370         return this.layout;
31371     },
31372     
31373      /**
31374      * Adds a xtype elements to the layout of the nested panel
31375      * <pre><code>
31376
31377 panel.addxtype({
31378        xtype : 'ContentPanel',
31379        region: 'west',
31380        items: [ .... ]
31381    }
31382 );
31383
31384 panel.addxtype({
31385         xtype : 'NestedLayoutPanel',
31386         region: 'west',
31387         layout: {
31388            center: { },
31389            west: { }   
31390         },
31391         items : [ ... list of content panels or nested layout panels.. ]
31392    }
31393 );
31394 </code></pre>
31395      * @param {Object} cfg Xtype definition of item to add.
31396      */
31397     addxtype : function(cfg) {
31398         return this.layout.addxtype(cfg);
31399     
31400     }
31401 });
31402
31403 Roo.ScrollPanel = function(el, config, content){
31404     config = config || {};
31405     config.fitToFrame = true;
31406     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31407     
31408     this.el.dom.style.overflow = "hidden";
31409     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31410     this.el.removeClass("x-layout-inactive-content");
31411     this.el.on("mousewheel", this.onWheel, this);
31412
31413     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31414     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31415     up.unselectable(); down.unselectable();
31416     up.on("click", this.scrollUp, this);
31417     down.on("click", this.scrollDown, this);
31418     up.addClassOnOver("x-scroller-btn-over");
31419     down.addClassOnOver("x-scroller-btn-over");
31420     up.addClassOnClick("x-scroller-btn-click");
31421     down.addClassOnClick("x-scroller-btn-click");
31422     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31423
31424     this.resizeEl = this.el;
31425     this.el = wrap; this.up = up; this.down = down;
31426 };
31427
31428 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31429     increment : 100,
31430     wheelIncrement : 5,
31431     scrollUp : function(){
31432         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31433     },
31434
31435     scrollDown : function(){
31436         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31437     },
31438
31439     afterScroll : function(){
31440         var el = this.resizeEl;
31441         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31442         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31443         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31444     },
31445
31446     setSize : function(){
31447         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31448         this.afterScroll();
31449     },
31450
31451     onWheel : function(e){
31452         var d = e.getWheelDelta();
31453         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31454         this.afterScroll();
31455         e.stopEvent();
31456     },
31457
31458     setContent : function(content, loadScripts){
31459         this.resizeEl.update(content, loadScripts);
31460     }
31461
31462 });
31463
31464
31465
31466
31467
31468
31469
31470
31471
31472 /**
31473  * @class Roo.TreePanel
31474  * @extends Roo.ContentPanel
31475  * @constructor
31476  * Create a new TreePanel. - defaults to fit/scoll contents.
31477  * @param {String/Object} config A string to set only the panel's title, or a config object
31478  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31479  */
31480 Roo.TreePanel = function(config){
31481     var el = config.el;
31482     var tree = config.tree;
31483     delete config.tree; 
31484     delete config.el; // hopefull!
31485     
31486     // wrapper for IE7 strict & safari scroll issue
31487     
31488     var treeEl = el.createChild();
31489     config.resizeEl = treeEl;
31490     
31491     
31492     
31493     Roo.TreePanel.superclass.constructor.call(this, el, config);
31494  
31495  
31496     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31497     //console.log(tree);
31498     this.on('activate', function()
31499     {
31500         if (this.tree.rendered) {
31501             return;
31502         }
31503         //console.log('render tree');
31504         this.tree.render();
31505     });
31506     
31507     this.on('resize',  function (cp, w, h) {
31508             this.tree.innerCt.setWidth(w);
31509             this.tree.innerCt.setHeight(h);
31510             this.tree.innerCt.setStyle('overflow-y', 'auto');
31511     });
31512
31513         
31514     
31515 };
31516
31517 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31518     fitToFrame : true,
31519     autoScroll : true
31520 });
31521
31522
31523
31524
31525
31526
31527
31528
31529
31530
31531
31532 /*
31533  * Based on:
31534  * Ext JS Library 1.1.1
31535  * Copyright(c) 2006-2007, Ext JS, LLC.
31536  *
31537  * Originally Released Under LGPL - original licence link has changed is not relivant.
31538  *
31539  * Fork - LGPL
31540  * <script type="text/javascript">
31541  */
31542  
31543
31544 /**
31545  * @class Roo.ReaderLayout
31546  * @extends Roo.BorderLayout
31547  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31548  * center region containing two nested regions (a top one for a list view and one for item preview below),
31549  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31550  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31551  * expedites the setup of the overall layout and regions for this common application style.
31552  * Example:
31553  <pre><code>
31554 var reader = new Roo.ReaderLayout();
31555 var CP = Roo.ContentPanel;  // shortcut for adding
31556
31557 reader.beginUpdate();
31558 reader.add("north", new CP("north", "North"));
31559 reader.add("west", new CP("west", {title: "West"}));
31560 reader.add("east", new CP("east", {title: "East"}));
31561
31562 reader.regions.listView.add(new CP("listView", "List"));
31563 reader.regions.preview.add(new CP("preview", "Preview"));
31564 reader.endUpdate();
31565 </code></pre>
31566 * @constructor
31567 * Create a new ReaderLayout
31568 * @param {Object} config Configuration options
31569 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31570 * document.body if omitted)
31571 */
31572 Roo.ReaderLayout = function(config, renderTo){
31573     var c = config || {size:{}};
31574     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31575         north: c.north !== false ? Roo.apply({
31576             split:false,
31577             initialSize: 32,
31578             titlebar: false
31579         }, c.north) : false,
31580         west: c.west !== false ? Roo.apply({
31581             split:true,
31582             initialSize: 200,
31583             minSize: 175,
31584             maxSize: 400,
31585             titlebar: true,
31586             collapsible: true,
31587             animate: true,
31588             margins:{left:5,right:0,bottom:5,top:5},
31589             cmargins:{left:5,right:5,bottom:5,top:5}
31590         }, c.west) : false,
31591         east: c.east !== false ? Roo.apply({
31592             split:true,
31593             initialSize: 200,
31594             minSize: 175,
31595             maxSize: 400,
31596             titlebar: true,
31597             collapsible: true,
31598             animate: true,
31599             margins:{left:0,right:5,bottom:5,top:5},
31600             cmargins:{left:5,right:5,bottom:5,top:5}
31601         }, c.east) : false,
31602         center: Roo.apply({
31603             tabPosition: 'top',
31604             autoScroll:false,
31605             closeOnTab: true,
31606             titlebar:false,
31607             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31608         }, c.center)
31609     });
31610
31611     this.el.addClass('x-reader');
31612
31613     this.beginUpdate();
31614
31615     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31616         south: c.preview !== false ? Roo.apply({
31617             split:true,
31618             initialSize: 200,
31619             minSize: 100,
31620             autoScroll:true,
31621             collapsible:true,
31622             titlebar: true,
31623             cmargins:{top:5,left:0, right:0, bottom:0}
31624         }, c.preview) : false,
31625         center: Roo.apply({
31626             autoScroll:false,
31627             titlebar:false,
31628             minHeight:200
31629         }, c.listView)
31630     });
31631     this.add('center', new Roo.NestedLayoutPanel(inner,
31632             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31633
31634     this.endUpdate();
31635
31636     this.regions.preview = inner.getRegion('south');
31637     this.regions.listView = inner.getRegion('center');
31638 };
31639
31640 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31641  * Based on:
31642  * Ext JS Library 1.1.1
31643  * Copyright(c) 2006-2007, Ext JS, LLC.
31644  *
31645  * Originally Released Under LGPL - original licence link has changed is not relivant.
31646  *
31647  * Fork - LGPL
31648  * <script type="text/javascript">
31649  */
31650  
31651 /**
31652  * @class Roo.grid.Grid
31653  * @extends Roo.util.Observable
31654  * This class represents the primary interface of a component based grid control.
31655  * <br><br>Usage:<pre><code>
31656  var grid = new Roo.grid.Grid("my-container-id", {
31657      ds: myDataStore,
31658      cm: myColModel,
31659      selModel: mySelectionModel,
31660      autoSizeColumns: true,
31661      monitorWindowResize: false,
31662      trackMouseOver: true
31663  });
31664  // set any options
31665  grid.render();
31666  * </code></pre>
31667  * <b>Common Problems:</b><br/>
31668  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31669  * element will correct this<br/>
31670  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31671  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31672  * are unpredictable.<br/>
31673  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31674  * grid to calculate dimensions/offsets.<br/>
31675   * @constructor
31676  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31677  * The container MUST have some type of size defined for the grid to fill. The container will be
31678  * automatically set to position relative if it isn't already.
31679  * @param {Object} config A config object that sets properties on this grid.
31680  */
31681 Roo.grid.Grid = function(container, config){
31682         // initialize the container
31683         this.container = Roo.get(container);
31684         this.container.update("");
31685         this.container.setStyle("overflow", "hidden");
31686     this.container.addClass('x-grid-container');
31687
31688     this.id = this.container.id;
31689
31690     Roo.apply(this, config);
31691     // check and correct shorthanded configs
31692     if(this.ds){
31693         this.dataSource = this.ds;
31694         delete this.ds;
31695     }
31696     if(this.cm){
31697         this.colModel = this.cm;
31698         delete this.cm;
31699     }
31700     if(this.sm){
31701         this.selModel = this.sm;
31702         delete this.sm;
31703     }
31704
31705     if (this.selModel) {
31706         this.selModel = Roo.factory(this.selModel, Roo.grid);
31707         this.sm = this.selModel;
31708         this.sm.xmodule = this.xmodule || false;
31709     }
31710     if (typeof(this.colModel.config) == 'undefined') {
31711         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31712         this.cm = this.colModel;
31713         this.cm.xmodule = this.xmodule || false;
31714     }
31715     if (this.dataSource) {
31716         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31717         this.ds = this.dataSource;
31718         this.ds.xmodule = this.xmodule || false;
31719         
31720     }
31721     
31722     
31723     
31724     if(this.width){
31725         this.container.setWidth(this.width);
31726     }
31727
31728     if(this.height){
31729         this.container.setHeight(this.height);
31730     }
31731     /** @private */
31732         this.addEvents({
31733             // raw events
31734             /**
31735              * @event click
31736              * The raw click event for the entire grid.
31737              * @param {Roo.EventObject} e
31738              */
31739             "click" : true,
31740             /**
31741              * @event dblclick
31742              * The raw dblclick event for the entire grid.
31743              * @param {Roo.EventObject} e
31744              */
31745             "dblclick" : true,
31746             /**
31747              * @event contextmenu
31748              * The raw contextmenu event for the entire grid.
31749              * @param {Roo.EventObject} e
31750              */
31751             "contextmenu" : true,
31752             /**
31753              * @event mousedown
31754              * The raw mousedown event for the entire grid.
31755              * @param {Roo.EventObject} e
31756              */
31757             "mousedown" : true,
31758             /**
31759              * @event mouseup
31760              * The raw mouseup event for the entire grid.
31761              * @param {Roo.EventObject} e
31762              */
31763             "mouseup" : true,
31764             /**
31765              * @event mouseover
31766              * The raw mouseover event for the entire grid.
31767              * @param {Roo.EventObject} e
31768              */
31769             "mouseover" : true,
31770             /**
31771              * @event mouseout
31772              * The raw mouseout event for the entire grid.
31773              * @param {Roo.EventObject} e
31774              */
31775             "mouseout" : true,
31776             /**
31777              * @event keypress
31778              * The raw keypress event for the entire grid.
31779              * @param {Roo.EventObject} e
31780              */
31781             "keypress" : true,
31782             /**
31783              * @event keydown
31784              * The raw keydown event for the entire grid.
31785              * @param {Roo.EventObject} e
31786              */
31787             "keydown" : true,
31788
31789             // custom events
31790
31791             /**
31792              * @event cellclick
31793              * Fires when a cell is clicked
31794              * @param {Grid} this
31795              * @param {Number} rowIndex
31796              * @param {Number} columnIndex
31797              * @param {Roo.EventObject} e
31798              */
31799             "cellclick" : true,
31800             /**
31801              * @event celldblclick
31802              * Fires when a cell is double clicked
31803              * @param {Grid} this
31804              * @param {Number} rowIndex
31805              * @param {Number} columnIndex
31806              * @param {Roo.EventObject} e
31807              */
31808             "celldblclick" : true,
31809             /**
31810              * @event rowclick
31811              * Fires when a row is clicked
31812              * @param {Grid} this
31813              * @param {Number} rowIndex
31814              * @param {Roo.EventObject} e
31815              */
31816             "rowclick" : true,
31817             /**
31818              * @event rowdblclick
31819              * Fires when a row is double clicked
31820              * @param {Grid} this
31821              * @param {Number} rowIndex
31822              * @param {Roo.EventObject} e
31823              */
31824             "rowdblclick" : true,
31825             /**
31826              * @event headerclick
31827              * Fires when a header is clicked
31828              * @param {Grid} this
31829              * @param {Number} columnIndex
31830              * @param {Roo.EventObject} e
31831              */
31832             "headerclick" : true,
31833             /**
31834              * @event headerdblclick
31835              * Fires when a header cell is double clicked
31836              * @param {Grid} this
31837              * @param {Number} columnIndex
31838              * @param {Roo.EventObject} e
31839              */
31840             "headerdblclick" : true,
31841             /**
31842              * @event rowcontextmenu
31843              * Fires when a row is right clicked
31844              * @param {Grid} this
31845              * @param {Number} rowIndex
31846              * @param {Roo.EventObject} e
31847              */
31848             "rowcontextmenu" : true,
31849             /**
31850          * @event cellcontextmenu
31851          * Fires when a cell is right clicked
31852          * @param {Grid} this
31853          * @param {Number} rowIndex
31854          * @param {Number} cellIndex
31855          * @param {Roo.EventObject} e
31856          */
31857          "cellcontextmenu" : true,
31858             /**
31859              * @event headercontextmenu
31860              * Fires when a header is right clicked
31861              * @param {Grid} this
31862              * @param {Number} columnIndex
31863              * @param {Roo.EventObject} e
31864              */
31865             "headercontextmenu" : true,
31866             /**
31867              * @event bodyscroll
31868              * Fires when the body element is scrolled
31869              * @param {Number} scrollLeft
31870              * @param {Number} scrollTop
31871              */
31872             "bodyscroll" : true,
31873             /**
31874              * @event columnresize
31875              * Fires when the user resizes a column
31876              * @param {Number} columnIndex
31877              * @param {Number} newSize
31878              */
31879             "columnresize" : true,
31880             /**
31881              * @event columnmove
31882              * Fires when the user moves a column
31883              * @param {Number} oldIndex
31884              * @param {Number} newIndex
31885              */
31886             "columnmove" : true,
31887             /**
31888              * @event startdrag
31889              * Fires when row(s) start being dragged
31890              * @param {Grid} this
31891              * @param {Roo.GridDD} dd The drag drop object
31892              * @param {event} e The raw browser event
31893              */
31894             "startdrag" : true,
31895             /**
31896              * @event enddrag
31897              * Fires when a drag operation is complete
31898              * @param {Grid} this
31899              * @param {Roo.GridDD} dd The drag drop object
31900              * @param {event} e The raw browser event
31901              */
31902             "enddrag" : true,
31903             /**
31904              * @event dragdrop
31905              * Fires when dragged row(s) are dropped on a valid DD target
31906              * @param {Grid} this
31907              * @param {Roo.GridDD} dd The drag drop object
31908              * @param {String} targetId The target drag drop object
31909              * @param {event} e The raw browser event
31910              */
31911             "dragdrop" : true,
31912             /**
31913              * @event dragover
31914              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31915              * @param {Grid} this
31916              * @param {Roo.GridDD} dd The drag drop object
31917              * @param {String} targetId The target drag drop object
31918              * @param {event} e The raw browser event
31919              */
31920             "dragover" : true,
31921             /**
31922              * @event dragenter
31923              *  Fires when the dragged row(s) first cross another DD target while being dragged
31924              * @param {Grid} this
31925              * @param {Roo.GridDD} dd The drag drop object
31926              * @param {String} targetId The target drag drop object
31927              * @param {event} e The raw browser event
31928              */
31929             "dragenter" : true,
31930             /**
31931              * @event dragout
31932              * Fires when the dragged row(s) leave another DD target while being dragged
31933              * @param {Grid} this
31934              * @param {Roo.GridDD} dd The drag drop object
31935              * @param {String} targetId The target drag drop object
31936              * @param {event} e The raw browser event
31937              */
31938             "dragout" : true,
31939         /**
31940          * @event render
31941          * Fires when the grid is rendered
31942          * @param {Grid} grid
31943          */
31944         render : true
31945     });
31946
31947     Roo.grid.Grid.superclass.constructor.call(this);
31948 };
31949 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31950     
31951     /**
31952      * @cfg {String} ddGroup - drag drop group.
31953          */
31954     
31955     /**
31956      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31957          */
31958         minColumnWidth : 25,
31959
31960     /**
31961          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31962          * <b>on initial render.</b> It is more efficient to explicitly size the columns
31963          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31964          */
31965         autoSizeColumns : false,
31966
31967         /**
31968          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31969          */
31970         autoSizeHeaders : true,
31971
31972         /**
31973          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31974          */
31975         monitorWindowResize : true,
31976
31977         /**
31978          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31979          * rows measured to get a columns size. Default is 0 (all rows).
31980          */
31981         maxRowsToMeasure : 0,
31982
31983         /**
31984          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31985          */
31986         trackMouseOver : true,
31987
31988     /**
31989          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
31990          */
31991     
31992         /**
31993          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
31994          */
31995         enableDragDrop : false,
31996
31997         /**
31998          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
31999          */
32000         enableColumnMove : true,
32001
32002         /**
32003          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32004          */
32005         enableColumnHide : true,
32006
32007         /**
32008          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32009          */
32010         enableRowHeightSync : false,
32011
32012         /**
32013          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32014          */
32015         stripeRows : true,
32016
32017         /**
32018          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32019          */
32020         autoHeight : false,
32021
32022     /**
32023      * @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.
32024      */
32025     autoExpandColumn : false,
32026
32027     /**
32028     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32029     * Default is 50.
32030     */
32031     autoExpandMin : 50,
32032
32033     /**
32034     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32035     */
32036     autoExpandMax : 1000,
32037
32038     /**
32039          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32040          */
32041         view : null,
32042
32043         /**
32044      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32045          */
32046         loadMask : false,
32047
32048     // private
32049     rendered : false,
32050
32051     /**
32052     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32053     * of a fixed width. Default is false.
32054     */
32055     /**
32056     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32057     */
32058     /**
32059      * Called once after all setup has been completed and the grid is ready to be rendered.
32060      * @return {Roo.grid.Grid} this
32061      */
32062     render : function(){
32063         var c = this.container;
32064         // try to detect autoHeight/width mode
32065         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32066             this.autoHeight = true;
32067         }
32068         var view = this.getView();
32069         view.init(this);
32070
32071         c.on("click", this.onClick, this);
32072         c.on("dblclick", this.onDblClick, this);
32073         c.on("contextmenu", this.onContextMenu, this);
32074         c.on("keydown", this.onKeyDown, this);
32075
32076         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32077
32078         this.getSelectionModel().init(this);
32079
32080         view.render();
32081
32082         if(this.loadMask){
32083             this.loadMask = new Roo.LoadMask(this.container,
32084                     Roo.apply({store:this.dataSource}, this.loadMask));
32085         }
32086         
32087         
32088         if (this.toolbar && this.toolbar.xtype) {
32089             this.toolbar.container = this.getView().getHeaderPanel(true);
32090             this.toolbar = new Ext.Toolbar(this.toolbar);
32091         }
32092         if (this.footer && this.footer.xtype) {
32093             this.footer.dataSource = this.getDataSource();
32094             this.footer.container = this.getView().getFooterPanel(true);
32095             this.footer = Roo.factory(this.footer, Roo);
32096         }
32097         this.rendered = true;
32098         this.fireEvent('render', this);
32099         return this;
32100     },
32101
32102         /**
32103          * Reconfigures the grid to use a different Store and Column Model.
32104          * The View will be bound to the new objects and refreshed.
32105          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32106          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32107          */
32108     reconfigure : function(dataSource, colModel){
32109         if(this.loadMask){
32110             this.loadMask.destroy();
32111             this.loadMask = new Roo.LoadMask(this.container,
32112                     Roo.apply({store:dataSource}, this.loadMask));
32113         }
32114         this.view.bind(dataSource, colModel);
32115         this.dataSource = dataSource;
32116         this.colModel = colModel;
32117         this.view.refresh(true);
32118     },
32119
32120     // private
32121     onKeyDown : function(e){
32122         this.fireEvent("keydown", e);
32123     },
32124
32125     /**
32126      * Destroy this grid.
32127      * @param {Boolean} removeEl True to remove the element
32128      */
32129     destroy : function(removeEl, keepListeners){
32130         if(this.loadMask){
32131             this.loadMask.destroy();
32132         }
32133         var c = this.container;
32134         c.removeAllListeners();
32135         this.view.destroy();
32136         this.colModel.purgeListeners();
32137         if(!keepListeners){
32138             this.purgeListeners();
32139         }
32140         c.update("");
32141         if(removeEl === true){
32142             c.remove();
32143         }
32144     },
32145
32146     // private
32147     processEvent : function(name, e){
32148         this.fireEvent(name, e);
32149         var t = e.getTarget();
32150         var v = this.view;
32151         var header = v.findHeaderIndex(t);
32152         if(header !== false){
32153             this.fireEvent("header" + name, this, header, e);
32154         }else{
32155             var row = v.findRowIndex(t);
32156             var cell = v.findCellIndex(t);
32157             if(row !== false){
32158                 this.fireEvent("row" + name, this, row, e);
32159                 if(cell !== false){
32160                     this.fireEvent("cell" + name, this, row, cell, e);
32161                 }
32162             }
32163         }
32164     },
32165
32166     // private
32167     onClick : function(e){
32168         this.processEvent("click", e);
32169     },
32170
32171     // private
32172     onContextMenu : function(e, t){
32173         this.processEvent("contextmenu", e);
32174     },
32175
32176     // private
32177     onDblClick : function(e){
32178         this.processEvent("dblclick", e);
32179     },
32180
32181     // private
32182     walkCells : function(row, col, step, fn, scope){
32183         var cm = this.colModel, clen = cm.getColumnCount();
32184         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32185         if(step < 0){
32186             if(col < 0){
32187                 row--;
32188                 first = false;
32189             }
32190             while(row >= 0){
32191                 if(!first){
32192                     col = clen-1;
32193                 }
32194                 first = false;
32195                 while(col >= 0){
32196                     if(fn.call(scope || this, row, col, cm) === true){
32197                         return [row, col];
32198                     }
32199                     col--;
32200                 }
32201                 row--;
32202             }
32203         } else {
32204             if(col >= clen){
32205                 row++;
32206                 first = false;
32207             }
32208             while(row < rlen){
32209                 if(!first){
32210                     col = 0;
32211                 }
32212                 first = false;
32213                 while(col < clen){
32214                     if(fn.call(scope || this, row, col, cm) === true){
32215                         return [row, col];
32216                     }
32217                     col++;
32218                 }
32219                 row++;
32220             }
32221         }
32222         return null;
32223     },
32224
32225     // private
32226     getSelections : function(){
32227         return this.selModel.getSelections();
32228     },
32229
32230     /**
32231      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32232      * but if manual update is required this method will initiate it.
32233      */
32234     autoSize : function(){
32235         if(this.rendered){
32236             this.view.layout();
32237             if(this.view.adjustForScroll){
32238                 this.view.adjustForScroll();
32239             }
32240         }
32241     },
32242
32243     /**
32244      * Returns the grid's underlying element.
32245      * @return {Element} The element
32246      */
32247     getGridEl : function(){
32248         return this.container;
32249     },
32250
32251     // private for compatibility, overridden by editor grid
32252     stopEditing : function(){},
32253
32254     /**
32255      * Returns the grid's SelectionModel.
32256      * @return {SelectionModel}
32257      */
32258     getSelectionModel : function(){
32259         if(!this.selModel){
32260             this.selModel = new Roo.grid.RowSelectionModel();
32261         }
32262         return this.selModel;
32263     },
32264
32265     /**
32266      * Returns the grid's DataSource.
32267      * @return {DataSource}
32268      */
32269     getDataSource : function(){
32270         return this.dataSource;
32271     },
32272
32273     /**
32274      * Returns the grid's ColumnModel.
32275      * @return {ColumnModel}
32276      */
32277     getColumnModel : function(){
32278         return this.colModel;
32279     },
32280
32281     /**
32282      * Returns the grid's GridView object.
32283      * @return {GridView}
32284      */
32285     getView : function(){
32286         if(!this.view){
32287             this.view = new Roo.grid.GridView(this.viewConfig);
32288         }
32289         return this.view;
32290     },
32291     /**
32292      * Called to get grid's drag proxy text, by default returns this.ddText.
32293      * @return {String}
32294      */
32295     getDragDropText : function(){
32296         var count = this.selModel.getCount();
32297         return String.format(this.ddText, count, count == 1 ? '' : 's');
32298     }
32299 });
32300 /**
32301  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32302  * %0 is replaced with the number of selected rows.
32303  * @type String
32304  */
32305 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32306  * Based on:
32307  * Ext JS Library 1.1.1
32308  * Copyright(c) 2006-2007, Ext JS, LLC.
32309  *
32310  * Originally Released Under LGPL - original licence link has changed is not relivant.
32311  *
32312  * Fork - LGPL
32313  * <script type="text/javascript">
32314  */
32315  
32316 Roo.grid.AbstractGridView = function(){
32317         this.grid = null;
32318         
32319         this.events = {
32320             "beforerowremoved" : true,
32321             "beforerowsinserted" : true,
32322             "beforerefresh" : true,
32323             "rowremoved" : true,
32324             "rowsinserted" : true,
32325             "rowupdated" : true,
32326             "refresh" : true
32327         };
32328     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32329 };
32330
32331 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32332     rowClass : "x-grid-row",
32333     cellClass : "x-grid-cell",
32334     tdClass : "x-grid-td",
32335     hdClass : "x-grid-hd",
32336     splitClass : "x-grid-hd-split",
32337     
32338         init: function(grid){
32339         this.grid = grid;
32340                 var cid = this.grid.getGridEl().id;
32341         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32342         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32343         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32344         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32345         },
32346         
32347         getColumnRenderers : function(){
32348         var renderers = [];
32349         var cm = this.grid.colModel;
32350         var colCount = cm.getColumnCount();
32351         for(var i = 0; i < colCount; i++){
32352             renderers[i] = cm.getRenderer(i);
32353         }
32354         return renderers;
32355     },
32356     
32357     getColumnIds : function(){
32358         var ids = [];
32359         var cm = this.grid.colModel;
32360         var colCount = cm.getColumnCount();
32361         for(var i = 0; i < colCount; i++){
32362             ids[i] = cm.getColumnId(i);
32363         }
32364         return ids;
32365     },
32366     
32367     getDataIndexes : function(){
32368         if(!this.indexMap){
32369             this.indexMap = this.buildIndexMap();
32370         }
32371         return this.indexMap.colToData;
32372     },
32373     
32374     getColumnIndexByDataIndex : function(dataIndex){
32375         if(!this.indexMap){
32376             this.indexMap = this.buildIndexMap();
32377         }
32378         return this.indexMap.dataToCol[dataIndex];
32379     },
32380     
32381     /**
32382      * Set a css style for a column dynamically. 
32383      * @param {Number} colIndex The index of the column
32384      * @param {String} name The css property name
32385      * @param {String} value The css value
32386      */
32387     setCSSStyle : function(colIndex, name, value){
32388         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32389         Roo.util.CSS.updateRule(selector, name, value);
32390     },
32391     
32392     generateRules : function(cm){
32393         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32394         Roo.util.CSS.removeStyleSheet(rulesId);
32395         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32396             var cid = cm.getColumnId(i);
32397             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32398                          this.tdSelector, cid, " {\n}\n",
32399                          this.hdSelector, cid, " {\n}\n",
32400                          this.splitSelector, cid, " {\n}\n");
32401         }
32402         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32403     }
32404 });/*
32405  * Based on:
32406  * Ext JS Library 1.1.1
32407  * Copyright(c) 2006-2007, Ext JS, LLC.
32408  *
32409  * Originally Released Under LGPL - original licence link has changed is not relivant.
32410  *
32411  * Fork - LGPL
32412  * <script type="text/javascript">
32413  */
32414
32415 // private
32416 // This is a support class used internally by the Grid components
32417 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32418     this.grid = grid;
32419     this.view = grid.getView();
32420     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32421     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32422     if(hd2){
32423         this.setHandleElId(Roo.id(hd));
32424         this.setOuterHandleElId(Roo.id(hd2));
32425     }
32426     this.scroll = false;
32427 };
32428 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32429     maxDragWidth: 120,
32430     getDragData : function(e){
32431         var t = Roo.lib.Event.getTarget(e);
32432         var h = this.view.findHeaderCell(t);
32433         if(h){
32434             return {ddel: h.firstChild, header:h};
32435         }
32436         return false;
32437     },
32438
32439     onInitDrag : function(e){
32440         this.view.headersDisabled = true;
32441         var clone = this.dragData.ddel.cloneNode(true);
32442         clone.id = Roo.id();
32443         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32444         this.proxy.update(clone);
32445         return true;
32446     },
32447
32448     afterValidDrop : function(){
32449         var v = this.view;
32450         setTimeout(function(){
32451             v.headersDisabled = false;
32452         }, 50);
32453     },
32454
32455     afterInvalidDrop : function(){
32456         var v = this.view;
32457         setTimeout(function(){
32458             v.headersDisabled = false;
32459         }, 50);
32460     }
32461 });
32462 /*
32463  * Based on:
32464  * Ext JS Library 1.1.1
32465  * Copyright(c) 2006-2007, Ext JS, LLC.
32466  *
32467  * Originally Released Under LGPL - original licence link has changed is not relivant.
32468  *
32469  * Fork - LGPL
32470  * <script type="text/javascript">
32471  */
32472 // private
32473 // This is a support class used internally by the Grid components
32474 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32475     this.grid = grid;
32476     this.view = grid.getView();
32477     // split the proxies so they don't interfere with mouse events
32478     this.proxyTop = Roo.DomHelper.append(document.body, {
32479         cls:"col-move-top", html:"&#160;"
32480     }, true);
32481     this.proxyBottom = Roo.DomHelper.append(document.body, {
32482         cls:"col-move-bottom", html:"&#160;"
32483     }, true);
32484     this.proxyTop.hide = this.proxyBottom.hide = function(){
32485         this.setLeftTop(-100,-100);
32486         this.setStyle("visibility", "hidden");
32487     };
32488     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32489     // temporarily disabled
32490     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32491     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32492 };
32493 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32494     proxyOffsets : [-4, -9],
32495     fly: Roo.Element.fly,
32496
32497     getTargetFromEvent : function(e){
32498         var t = Roo.lib.Event.getTarget(e);
32499         var cindex = this.view.findCellIndex(t);
32500         if(cindex !== false){
32501             return this.view.getHeaderCell(cindex);
32502         }
32503     },
32504
32505     nextVisible : function(h){
32506         var v = this.view, cm = this.grid.colModel;
32507         h = h.nextSibling;
32508         while(h){
32509             if(!cm.isHidden(v.getCellIndex(h))){
32510                 return h;
32511             }
32512             h = h.nextSibling;
32513         }
32514         return null;
32515     },
32516
32517     prevVisible : function(h){
32518         var v = this.view, cm = this.grid.colModel;
32519         h = h.prevSibling;
32520         while(h){
32521             if(!cm.isHidden(v.getCellIndex(h))){
32522                 return h;
32523             }
32524             h = h.prevSibling;
32525         }
32526         return null;
32527     },
32528
32529     positionIndicator : function(h, n, e){
32530         var x = Roo.lib.Event.getPageX(e);
32531         var r = Roo.lib.Dom.getRegion(n.firstChild);
32532         var px, pt, py = r.top + this.proxyOffsets[1];
32533         if((r.right - x) <= (r.right-r.left)/2){
32534             px = r.right+this.view.borderWidth;
32535             pt = "after";
32536         }else{
32537             px = r.left;
32538             pt = "before";
32539         }
32540         var oldIndex = this.view.getCellIndex(h);
32541         var newIndex = this.view.getCellIndex(n);
32542
32543         if(this.grid.colModel.isFixed(newIndex)){
32544             return false;
32545         }
32546
32547         var locked = this.grid.colModel.isLocked(newIndex);
32548
32549         if(pt == "after"){
32550             newIndex++;
32551         }
32552         if(oldIndex < newIndex){
32553             newIndex--;
32554         }
32555         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32556             return false;
32557         }
32558         px +=  this.proxyOffsets[0];
32559         this.proxyTop.setLeftTop(px, py);
32560         this.proxyTop.show();
32561         if(!this.bottomOffset){
32562             this.bottomOffset = this.view.mainHd.getHeight();
32563         }
32564         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32565         this.proxyBottom.show();
32566         return pt;
32567     },
32568
32569     onNodeEnter : function(n, dd, e, data){
32570         if(data.header != n){
32571             this.positionIndicator(data.header, n, e);
32572         }
32573     },
32574
32575     onNodeOver : function(n, dd, e, data){
32576         var result = false;
32577         if(data.header != n){
32578             result = this.positionIndicator(data.header, n, e);
32579         }
32580         if(!result){
32581             this.proxyTop.hide();
32582             this.proxyBottom.hide();
32583         }
32584         return result ? this.dropAllowed : this.dropNotAllowed;
32585     },
32586
32587     onNodeOut : function(n, dd, e, data){
32588         this.proxyTop.hide();
32589         this.proxyBottom.hide();
32590     },
32591
32592     onNodeDrop : function(n, dd, e, data){
32593         var h = data.header;
32594         if(h != n){
32595             var cm = this.grid.colModel;
32596             var x = Roo.lib.Event.getPageX(e);
32597             var r = Roo.lib.Dom.getRegion(n.firstChild);
32598             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32599             var oldIndex = this.view.getCellIndex(h);
32600             var newIndex = this.view.getCellIndex(n);
32601             var locked = cm.isLocked(newIndex);
32602             if(pt == "after"){
32603                 newIndex++;
32604             }
32605             if(oldIndex < newIndex){
32606                 newIndex--;
32607             }
32608             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32609                 return false;
32610             }
32611             cm.setLocked(oldIndex, locked, true);
32612             cm.moveColumn(oldIndex, newIndex);
32613             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32614             return true;
32615         }
32616         return false;
32617     }
32618 });
32619 /*
32620  * Based on:
32621  * Ext JS Library 1.1.1
32622  * Copyright(c) 2006-2007, Ext JS, LLC.
32623  *
32624  * Originally Released Under LGPL - original licence link has changed is not relivant.
32625  *
32626  * Fork - LGPL
32627  * <script type="text/javascript">
32628  */
32629   
32630 /**
32631  * @class Roo.grid.GridView
32632  * @extends Roo.util.Observable
32633  *
32634  * @constructor
32635  * @param {Object} config
32636  */
32637 Roo.grid.GridView = function(config){
32638     Roo.grid.GridView.superclass.constructor.call(this);
32639     this.el = null;
32640
32641     Roo.apply(this, config);
32642 };
32643
32644 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32645
32646     /**
32647      * Override this function to apply custom css classes to rows during rendering
32648      * @param {Record} record The record
32649      * @param {Number} index
32650      * @method getRowClass
32651      */
32652     rowClass : "x-grid-row",
32653
32654     cellClass : "x-grid-col",
32655
32656     tdClass : "x-grid-td",
32657
32658     hdClass : "x-grid-hd",
32659
32660     splitClass : "x-grid-split",
32661
32662     sortClasses : ["sort-asc", "sort-desc"],
32663
32664     enableMoveAnim : false,
32665
32666     hlColor: "C3DAF9",
32667
32668     dh : Roo.DomHelper,
32669
32670     fly : Roo.Element.fly,
32671
32672     css : Roo.util.CSS,
32673
32674     borderWidth: 1,
32675
32676     splitOffset: 3,
32677
32678     scrollIncrement : 22,
32679
32680     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32681
32682     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32683
32684     bind : function(ds, cm){
32685         if(this.ds){
32686             this.ds.un("load", this.onLoad, this);
32687             this.ds.un("datachanged", this.onDataChange, this);
32688             this.ds.un("add", this.onAdd, this);
32689             this.ds.un("remove", this.onRemove, this);
32690             this.ds.un("update", this.onUpdate, this);
32691             this.ds.un("clear", this.onClear, this);
32692         }
32693         if(ds){
32694             ds.on("load", this.onLoad, this);
32695             ds.on("datachanged", this.onDataChange, this);
32696             ds.on("add", this.onAdd, this);
32697             ds.on("remove", this.onRemove, this);
32698             ds.on("update", this.onUpdate, this);
32699             ds.on("clear", this.onClear, this);
32700         }
32701         this.ds = ds;
32702
32703         if(this.cm){
32704             this.cm.un("widthchange", this.onColWidthChange, this);
32705             this.cm.un("headerchange", this.onHeaderChange, this);
32706             this.cm.un("hiddenchange", this.onHiddenChange, this);
32707             this.cm.un("columnmoved", this.onColumnMove, this);
32708             this.cm.un("columnlockchange", this.onColumnLock, this);
32709         }
32710         if(cm){
32711             this.generateRules(cm);
32712             cm.on("widthchange", this.onColWidthChange, this);
32713             cm.on("headerchange", this.onHeaderChange, this);
32714             cm.on("hiddenchange", this.onHiddenChange, this);
32715             cm.on("columnmoved", this.onColumnMove, this);
32716             cm.on("columnlockchange", this.onColumnLock, this);
32717         }
32718         this.cm = cm;
32719     },
32720
32721     init: function(grid){
32722                 Roo.grid.GridView.superclass.init.call(this, grid);
32723
32724                 this.bind(grid.dataSource, grid.colModel);
32725
32726             grid.on("headerclick", this.handleHeaderClick, this);
32727
32728         if(grid.trackMouseOver){
32729             grid.on("mouseover", this.onRowOver, this);
32730                 grid.on("mouseout", this.onRowOut, this);
32731             }
32732             grid.cancelTextSelection = function(){};
32733                 this.gridId = grid.id;
32734
32735                 var tpls = this.templates || {};
32736
32737                 if(!tpls.master){
32738                     tpls.master = new Roo.Template(
32739                        '<div class="x-grid" hidefocus="true">',
32740                           '<div class="x-grid-topbar"></div>',
32741                           '<div class="x-grid-scroller"><div></div></div>',
32742                           '<div class="x-grid-locked">',
32743                               '<div class="x-grid-header">{lockedHeader}</div>',
32744                               '<div class="x-grid-body">{lockedBody}</div>',
32745                           "</div>",
32746                           '<div class="x-grid-viewport">',
32747                               '<div class="x-grid-header">{header}</div>',
32748                               '<div class="x-grid-body">{body}</div>',
32749                           "</div>",
32750                           '<div class="x-grid-bottombar"></div>',
32751                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32752                           '<div class="x-grid-resize-proxy">&#160;</div>',
32753                        "</div>"
32754                     );
32755                     tpls.master.disableformats = true;
32756                 }
32757
32758                 if(!tpls.header){
32759                     tpls.header = new Roo.Template(
32760                        '<table border="0" cellspacing="0" cellpadding="0">',
32761                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32762                        "</table>{splits}"
32763                     );
32764                     tpls.header.disableformats = true;
32765                 }
32766                 tpls.header.compile();
32767
32768                 if(!tpls.hcell){
32769                     tpls.hcell = new Roo.Template(
32770                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32771                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32772                         "</div></td>"
32773                      );
32774                      tpls.hcell.disableFormats = true;
32775                 }
32776                 tpls.hcell.compile();
32777
32778                 if(!tpls.hsplit){
32779                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32780                     tpls.hsplit.disableFormats = true;
32781                 }
32782                 tpls.hsplit.compile();
32783
32784                 if(!tpls.body){
32785                     tpls.body = new Roo.Template(
32786                        '<table border="0" cellspacing="0" cellpadding="0">',
32787                        "<tbody>{rows}</tbody>",
32788                        "</table>"
32789                     );
32790                     tpls.body.disableFormats = true;
32791                 }
32792                 tpls.body.compile();
32793
32794                 if(!tpls.row){
32795                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32796                     tpls.row.disableFormats = true;
32797                 }
32798                 tpls.row.compile();
32799
32800                 if(!tpls.cell){
32801                     tpls.cell = new Roo.Template(
32802                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32803                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32804                         "</td>"
32805                     );
32806             tpls.cell.disableFormats = true;
32807         }
32808                 tpls.cell.compile();
32809
32810                 this.templates = tpls;
32811         },
32812
32813         // remap these for backwards compat
32814     onColWidthChange : function(){
32815         this.updateColumns.apply(this, arguments);
32816     },
32817     onHeaderChange : function(){
32818         this.updateHeaders.apply(this, arguments);
32819     }, 
32820     onHiddenChange : function(){
32821         this.handleHiddenChange.apply(this, arguments);
32822     },
32823     onColumnMove : function(){
32824         this.handleColumnMove.apply(this, arguments);
32825     },
32826     onColumnLock : function(){
32827         this.handleLockChange.apply(this, arguments);
32828     },
32829
32830     onDataChange : function(){
32831         this.refresh();
32832         this.updateHeaderSortState();
32833     },
32834
32835         onClear : function(){
32836         this.refresh();
32837     },
32838
32839         onUpdate : function(ds, record){
32840         this.refreshRow(record);
32841     },
32842
32843     refreshRow : function(record){
32844         var ds = this.ds, index;
32845         if(typeof record == 'number'){
32846             index = record;
32847             record = ds.getAt(index);
32848         }else{
32849             index = ds.indexOf(record);
32850         }
32851         this.insertRows(ds, index, index, true);
32852         this.onRemove(ds, record, index+1, true);
32853         this.syncRowHeights(index, index);
32854         this.layout();
32855         this.fireEvent("rowupdated", this, index, record);
32856     },
32857
32858     onAdd : function(ds, records, index){
32859         this.insertRows(ds, index, index + (records.length-1));
32860     },
32861
32862     onRemove : function(ds, record, index, isUpdate){
32863         if(isUpdate !== true){
32864             this.fireEvent("beforerowremoved", this, index, record);
32865         }
32866         var bt = this.getBodyTable(), lt = this.getLockedTable();
32867         if(bt.rows[index]){
32868             bt.firstChild.removeChild(bt.rows[index]);
32869         }
32870         if(lt.rows[index]){
32871             lt.firstChild.removeChild(lt.rows[index]);
32872         }
32873         if(isUpdate !== true){
32874             this.stripeRows(index);
32875             this.syncRowHeights(index, index);
32876             this.layout();
32877             this.fireEvent("rowremoved", this, index, record);
32878         }
32879     },
32880
32881     onLoad : function(){
32882         this.scrollToTop();
32883     },
32884
32885     /**
32886      * Scrolls the grid to the top
32887      */
32888     scrollToTop : function(){
32889         if(this.scroller){
32890             this.scroller.dom.scrollTop = 0;
32891             this.syncScroll();
32892         }
32893     },
32894
32895     /**
32896      * Gets a panel in the header of the grid that can be used for toolbars etc.
32897      * After modifying the contents of this panel a call to grid.autoSize() may be
32898      * required to register any changes in size.
32899      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32900      * @return Roo.Element
32901      */
32902     getHeaderPanel : function(doShow){
32903         if(doShow){
32904             this.headerPanel.show();
32905         }
32906         return this.headerPanel;
32907         },
32908
32909         /**
32910      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32911      * After modifying the contents of this panel a call to grid.autoSize() may be
32912      * required to register any changes in size.
32913      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32914      * @return Roo.Element
32915      */
32916     getFooterPanel : function(doShow){
32917         if(doShow){
32918             this.footerPanel.show();
32919         }
32920         return this.footerPanel;
32921         },
32922
32923         initElements : function(){
32924             var E = Roo.Element;
32925             var el = this.grid.getGridEl().dom.firstChild;
32926             var cs = el.childNodes;
32927
32928             this.el = new E(el);
32929             this.headerPanel = new E(el.firstChild);
32930             this.headerPanel.enableDisplayMode("block");
32931
32932         this.scroller = new E(cs[1]);
32933             this.scrollSizer = new E(this.scroller.dom.firstChild);
32934
32935             this.lockedWrap = new E(cs[2]);
32936             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
32937             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
32938
32939             this.mainWrap = new E(cs[3]);
32940             this.mainHd = new E(this.mainWrap.dom.firstChild);
32941             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
32942
32943             this.footerPanel = new E(cs[4]);
32944             this.footerPanel.enableDisplayMode("block");
32945
32946         this.focusEl = new E(cs[5]);
32947         this.focusEl.swallowEvent("click", true);
32948         this.resizeProxy = new E(cs[6]);
32949
32950             this.headerSelector = String.format(
32951                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
32952                this.lockedHd.id, this.mainHd.id
32953             );
32954
32955             this.splitterSelector = String.format(
32956                '#{0} div.x-grid-split, #{1} div.x-grid-split',
32957                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
32958             );
32959     },
32960     idToCssName : function(s)
32961     {
32962         return s.replace(/[^a-z0-9]+/ig, '-');
32963     },
32964
32965         getHeaderCell : function(index){
32966             return Roo.DomQuery.select(this.headerSelector)[index];
32967         },
32968
32969         getHeaderCellMeasure : function(index){
32970             return this.getHeaderCell(index).firstChild;
32971         },
32972
32973         getHeaderCellText : function(index){
32974             return this.getHeaderCell(index).firstChild.firstChild;
32975         },
32976
32977         getLockedTable : function(){
32978             return this.lockedBody.dom.firstChild;
32979         },
32980
32981         getBodyTable : function(){
32982             return this.mainBody.dom.firstChild;
32983         },
32984
32985         getLockedRow : function(index){
32986             return this.getLockedTable().rows[index];
32987         },
32988
32989         getRow : function(index){
32990             return this.getBodyTable().rows[index];
32991         },
32992
32993         getRowComposite : function(index){
32994             if(!this.rowEl){
32995                 this.rowEl = new Roo.CompositeElementLite();
32996             }
32997         var els = [], lrow, mrow;
32998         if(lrow = this.getLockedRow(index)){
32999             els.push(lrow);
33000         }
33001         if(mrow = this.getRow(index)){
33002             els.push(mrow);
33003         }
33004         this.rowEl.elements = els;
33005             return this.rowEl;
33006         },
33007
33008         getCell : function(rowIndex, colIndex){
33009             var locked = this.cm.getLockedCount();
33010             var source;
33011             if(colIndex < locked){
33012                 source = this.lockedBody.dom.firstChild;
33013             }else{
33014                 source = this.mainBody.dom.firstChild;
33015                 colIndex -= locked;
33016             }
33017         return source.rows[rowIndex].childNodes[colIndex];
33018         },
33019
33020         getCellText : function(rowIndex, colIndex){
33021             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33022         },
33023
33024         getCellBox : function(cell){
33025             var b = this.fly(cell).getBox();
33026         if(Roo.isOpera){ // opera fails to report the Y
33027             b.y = cell.offsetTop + this.mainBody.getY();
33028         }
33029         return b;
33030     },
33031
33032     getCellIndex : function(cell){
33033         var id = String(cell.className).match(this.cellRE);
33034         if(id){
33035             return parseInt(id[1], 10);
33036         }
33037         return 0;
33038     },
33039
33040     findHeaderIndex : function(n){
33041         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33042         return r ? this.getCellIndex(r) : false;
33043     },
33044
33045     findHeaderCell : function(n){
33046         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33047         return r ? r : false;
33048     },
33049
33050     findRowIndex : function(n){
33051         if(!n){
33052             return false;
33053         }
33054         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33055         return r ? r.rowIndex : false;
33056     },
33057
33058     findCellIndex : function(node){
33059         var stop = this.el.dom;
33060         while(node && node != stop){
33061             if(this.findRE.test(node.className)){
33062                 return this.getCellIndex(node);
33063             }
33064             node = node.parentNode;
33065         }
33066         return false;
33067     },
33068
33069     getColumnId : function(index){
33070             return this.cm.getColumnId(index);
33071         },
33072
33073         getSplitters : function(){
33074             if(this.splitterSelector){
33075                return Roo.DomQuery.select(this.splitterSelector);
33076             }else{
33077                 return null;
33078             }
33079         },
33080
33081         getSplitter : function(index){
33082             return this.getSplitters()[index];
33083         },
33084
33085     onRowOver : function(e, t){
33086         var row;
33087         if((row = this.findRowIndex(t)) !== false){
33088             this.getRowComposite(row).addClass("x-grid-row-over");
33089         }
33090     },
33091
33092     onRowOut : function(e, t){
33093         var row;
33094         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33095             this.getRowComposite(row).removeClass("x-grid-row-over");
33096         }
33097     },
33098
33099     renderHeaders : function(){
33100             var cm = this.cm;
33101         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33102         var cb = [], lb = [], sb = [], lsb = [], p = {};
33103         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33104             p.cellId = "x-grid-hd-0-" + i;
33105             p.splitId = "x-grid-csplit-0-" + i;
33106             p.id = cm.getColumnId(i);
33107             p.title = cm.getColumnTooltip(i) || "";
33108             p.value = cm.getColumnHeader(i) || "";
33109             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33110             if(!cm.isLocked(i)){
33111                 cb[cb.length] = ct.apply(p);
33112                 sb[sb.length] = st.apply(p);
33113             }else{
33114                 lb[lb.length] = ct.apply(p);
33115                 lsb[lsb.length] = st.apply(p);
33116             }
33117         }
33118         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33119                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33120         },
33121
33122         updateHeaders : function(){
33123         var html = this.renderHeaders();
33124         this.lockedHd.update(html[0]);
33125         this.mainHd.update(html[1]);
33126     },
33127
33128     /**
33129      * Focuses the specified row.
33130      * @param {Number} row The row index
33131      */
33132     focusRow : function(row){
33133         var x = this.scroller.dom.scrollLeft;
33134         this.focusCell(row, 0, false);
33135         this.scroller.dom.scrollLeft = x;
33136     },
33137
33138     /**
33139      * Focuses the specified cell.
33140      * @param {Number} row The row index
33141      * @param {Number} col The column index
33142      * @param {Boolean} hscroll false to disable horizontal scrolling
33143      */
33144     focusCell : function(row, col, hscroll){
33145         var el = this.ensureVisible(row, col, hscroll);
33146         this.focusEl.alignTo(el, "tl-tl");
33147         if(Roo.isGecko){
33148             this.focusEl.focus();
33149         }else{
33150             this.focusEl.focus.defer(1, this.focusEl);
33151         }
33152     },
33153
33154     /**
33155      * Scrolls the specified cell into view
33156      * @param {Number} row The row index
33157      * @param {Number} col The column index
33158      * @param {Boolean} hscroll false to disable horizontal scrolling
33159      */
33160     ensureVisible : function(row, col, hscroll){
33161         if(typeof row != "number"){
33162             row = row.rowIndex;
33163         }
33164         if(row < 0 && row >= this.ds.getCount()){
33165             return;
33166         }
33167         col = (col !== undefined ? col : 0);
33168         var cm = this.grid.colModel;
33169         while(cm.isHidden(col)){
33170             col++;
33171         }
33172
33173         var el = this.getCell(row, col);
33174         if(!el){
33175             return;
33176         }
33177         var c = this.scroller.dom;
33178
33179         var ctop = parseInt(el.offsetTop, 10);
33180         var cleft = parseInt(el.offsetLeft, 10);
33181         var cbot = ctop + el.offsetHeight;
33182         var cright = cleft + el.offsetWidth;
33183
33184         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33185         var stop = parseInt(c.scrollTop, 10);
33186         var sleft = parseInt(c.scrollLeft, 10);
33187         var sbot = stop + ch;
33188         var sright = sleft + c.clientWidth;
33189
33190         if(ctop < stop){
33191                 c.scrollTop = ctop;
33192         }else if(cbot > sbot){
33193             c.scrollTop = cbot-ch;
33194         }
33195
33196         if(hscroll !== false){
33197             if(cleft < sleft){
33198                 c.scrollLeft = cleft;
33199             }else if(cright > sright){
33200                 c.scrollLeft = cright-c.clientWidth;
33201             }
33202         }
33203         return el;
33204     },
33205
33206     updateColumns : function(){
33207         this.grid.stopEditing();
33208         var cm = this.grid.colModel, colIds = this.getColumnIds();
33209         //var totalWidth = cm.getTotalWidth();
33210         var pos = 0;
33211         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33212             //if(cm.isHidden(i)) continue;
33213             var w = cm.getColumnWidth(i);
33214             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33215             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33216         }
33217         this.updateSplitters();
33218     },
33219
33220     generateRules : function(cm){
33221         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33222         Roo.util.CSS.removeStyleSheet(rulesId);
33223         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33224             var cid = cm.getColumnId(i);
33225             var align = '';
33226             if(cm.config[i].align){
33227                 align = 'text-align:'+cm.config[i].align+';';
33228             }
33229             var hidden = '';
33230             if(cm.isHidden(i)){
33231                 hidden = 'display:none;';
33232             }
33233             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33234             ruleBuf.push(
33235                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33236                     this.hdSelector, cid, " {\n", align, width, "}\n",
33237                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33238                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33239         }
33240         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33241     },
33242
33243     updateSplitters : function(){
33244         var cm = this.cm, s = this.getSplitters();
33245         if(s){ // splitters not created yet
33246             var pos = 0, locked = true;
33247             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33248                 if(cm.isHidden(i)) continue;
33249                 var w = cm.getColumnWidth(i);
33250                 if(!cm.isLocked(i) && locked){
33251                     pos = 0;
33252                     locked = false;
33253                 }
33254                 pos += w;
33255                 s[i].style.left = (pos-this.splitOffset) + "px";
33256             }
33257         }
33258     },
33259
33260     handleHiddenChange : function(colModel, colIndex, hidden){
33261         if(hidden){
33262             this.hideColumn(colIndex);
33263         }else{
33264             this.unhideColumn(colIndex);
33265         }
33266     },
33267
33268     hideColumn : function(colIndex){
33269         var cid = this.getColumnId(colIndex);
33270         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33271         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33272         if(Roo.isSafari){
33273             this.updateHeaders();
33274         }
33275         this.updateSplitters();
33276         this.layout();
33277     },
33278
33279     unhideColumn : function(colIndex){
33280         var cid = this.getColumnId(colIndex);
33281         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33282         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33283
33284         if(Roo.isSafari){
33285             this.updateHeaders();
33286         }
33287         this.updateSplitters();
33288         this.layout();
33289     },
33290
33291     insertRows : function(dm, firstRow, lastRow, isUpdate){
33292         if(firstRow == 0 && lastRow == dm.getCount()-1){
33293             this.refresh();
33294         }else{
33295             if(!isUpdate){
33296                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33297             }
33298             var s = this.getScrollState();
33299             var markup = this.renderRows(firstRow, lastRow);
33300             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33301             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33302             this.restoreScroll(s);
33303             if(!isUpdate){
33304                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33305                 this.syncRowHeights(firstRow, lastRow);
33306                 this.stripeRows(firstRow);
33307                 this.layout();
33308             }
33309         }
33310     },
33311
33312     bufferRows : function(markup, target, index){
33313         var before = null, trows = target.rows, tbody = target.tBodies[0];
33314         if(index < trows.length){
33315             before = trows[index];
33316         }
33317         var b = document.createElement("div");
33318         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33319         var rows = b.firstChild.rows;
33320         for(var i = 0, len = rows.length; i < len; i++){
33321             if(before){
33322                 tbody.insertBefore(rows[0], before);
33323             }else{
33324                 tbody.appendChild(rows[0]);
33325             }
33326         }
33327         b.innerHTML = "";
33328         b = null;
33329     },
33330
33331     deleteRows : function(dm, firstRow, lastRow){
33332         if(dm.getRowCount()<1){
33333             this.fireEvent("beforerefresh", this);
33334             this.mainBody.update("");
33335             this.lockedBody.update("");
33336             this.fireEvent("refresh", this);
33337         }else{
33338             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33339             var bt = this.getBodyTable();
33340             var tbody = bt.firstChild;
33341             var rows = bt.rows;
33342             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33343                 tbody.removeChild(rows[firstRow]);
33344             }
33345             this.stripeRows(firstRow);
33346             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33347         }
33348     },
33349
33350     updateRows : function(dataSource, firstRow, lastRow){
33351         var s = this.getScrollState();
33352         this.refresh();
33353         this.restoreScroll(s);
33354     },
33355
33356     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33357         if(!noRefresh){
33358            this.refresh();
33359         }
33360         this.updateHeaderSortState();
33361     },
33362
33363     getScrollState : function(){
33364         var sb = this.scroller.dom;
33365         return {left: sb.scrollLeft, top: sb.scrollTop};
33366     },
33367
33368     stripeRows : function(startRow){
33369         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33370             return;
33371         }
33372         startRow = startRow || 0;
33373         var rows = this.getBodyTable().rows;
33374         var lrows = this.getLockedTable().rows;
33375         var cls = ' x-grid-row-alt ';
33376         for(var i = startRow, len = rows.length; i < len; i++){
33377             var row = rows[i], lrow = lrows[i];
33378             var isAlt = ((i+1) % 2 == 0);
33379             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33380             if(isAlt == hasAlt){
33381                 continue;
33382             }
33383             if(isAlt){
33384                 row.className += " x-grid-row-alt";
33385             }else{
33386                 row.className = row.className.replace("x-grid-row-alt", "");
33387             }
33388             if(lrow){
33389                 lrow.className = row.className;
33390             }
33391         }
33392     },
33393
33394     restoreScroll : function(state){
33395         var sb = this.scroller.dom;
33396         sb.scrollLeft = state.left;
33397         sb.scrollTop = state.top;
33398         this.syncScroll();
33399     },
33400
33401     syncScroll : function(){
33402         var sb = this.scroller.dom;
33403         var sh = this.mainHd.dom;
33404         var bs = this.mainBody.dom;
33405         var lv = this.lockedBody.dom;
33406         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33407         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33408     },
33409
33410     handleScroll : function(e){
33411         this.syncScroll();
33412         var sb = this.scroller.dom;
33413         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33414         e.stopEvent();
33415     },
33416
33417     handleWheel : function(e){
33418         var d = e.getWheelDelta();
33419         this.scroller.dom.scrollTop -= d*22;
33420         // set this here to prevent jumpy scrolling on large tables
33421         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33422         e.stopEvent();
33423     },
33424
33425     renderRows : function(startRow, endRow){
33426         // pull in all the crap needed to render rows
33427         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33428         var colCount = cm.getColumnCount();
33429
33430         if(ds.getCount() < 1){
33431             return ["", ""];
33432         }
33433
33434         // build a map for all the columns
33435         var cs = [];
33436         for(var i = 0; i < colCount; i++){
33437             var name = cm.getDataIndex(i);
33438             cs[i] = {
33439                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33440                 renderer : cm.getRenderer(i),
33441                 id : cm.getColumnId(i),
33442                 locked : cm.isLocked(i)
33443             };
33444         }
33445
33446         startRow = startRow || 0;
33447         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33448
33449         // records to render
33450         var rs = ds.getRange(startRow, endRow);
33451
33452         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33453     },
33454
33455     // As much as I hate to duplicate code, this was branched because FireFox really hates
33456     // [].join("") on strings. The performance difference was substantial enough to
33457     // branch this function
33458     doRender : Roo.isGecko ?
33459             function(cs, rs, ds, startRow, colCount, stripe){
33460                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33461                 // buffers
33462                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33463                 for(var j = 0, len = rs.length; j < len; j++){
33464                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33465                     for(var i = 0; i < colCount; i++){
33466                         c = cs[i];
33467                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33468                         p.id = c.id;
33469                         p.css = p.attr = "";
33470                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33471                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33472                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33473                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33474                         }
33475                         var markup = ct.apply(p);
33476                         if(!c.locked){
33477                             cb+= markup;
33478                         }else{
33479                             lcb+= markup;
33480                         }
33481                     }
33482                     var alt = [];
33483                     if(stripe && ((rowIndex+1) % 2 == 0)){
33484                         alt[0] = "x-grid-row-alt";
33485                     }
33486                     if(r.dirty){
33487                         alt[1] = " x-grid-dirty-row";
33488                     }
33489                     rp.cells = lcb;
33490                     if(this.getRowClass){
33491                         alt[2] = this.getRowClass(r, rowIndex);
33492                     }
33493                     rp.alt = alt.join(" ");
33494                     lbuf+= rt.apply(rp);
33495                     rp.cells = cb;
33496                     buf+=  rt.apply(rp);
33497                 }
33498                 return [lbuf, buf];
33499             } :
33500             function(cs, rs, ds, startRow, colCount, stripe){
33501                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33502                 // buffers
33503                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33504                 for(var j = 0, len = rs.length; j < len; j++){
33505                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33506                     for(var i = 0; i < colCount; i++){
33507                         c = cs[i];
33508                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33509                         p.id = c.id;
33510                         p.css = p.attr = "";
33511                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33512                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33513                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33514                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33515                         }
33516                         var markup = ct.apply(p);
33517                         if(!c.locked){
33518                             cb[cb.length] = markup;
33519                         }else{
33520                             lcb[lcb.length] = markup;
33521                         }
33522                     }
33523                     var alt = [];
33524                     if(stripe && ((rowIndex+1) % 2 == 0)){
33525                         alt[0] = "x-grid-row-alt";
33526                     }
33527                     if(r.dirty){
33528                         alt[1] = " x-grid-dirty-row";
33529                     }
33530                     rp.cells = lcb;
33531                     if(this.getRowClass){
33532                         alt[2] = this.getRowClass(r, rowIndex);
33533                     }
33534                     rp.alt = alt.join(" ");
33535                     rp.cells = lcb.join("");
33536                     lbuf[lbuf.length] = rt.apply(rp);
33537                     rp.cells = cb.join("");
33538                     buf[buf.length] =  rt.apply(rp);
33539                 }
33540                 return [lbuf.join(""), buf.join("")];
33541             },
33542
33543     renderBody : function(){
33544         var markup = this.renderRows();
33545         var bt = this.templates.body;
33546         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33547     },
33548
33549     /**
33550      * Refreshes the grid
33551      * @param {Boolean} headersToo
33552      */
33553     refresh : function(headersToo){
33554         this.fireEvent("beforerefresh", this);
33555         this.grid.stopEditing();
33556         var result = this.renderBody();
33557         this.lockedBody.update(result[0]);
33558         this.mainBody.update(result[1]);
33559         if(headersToo === true){
33560             this.updateHeaders();
33561             this.updateColumns();
33562             this.updateSplitters();
33563             this.updateHeaderSortState();
33564         }
33565         this.syncRowHeights();
33566         this.layout();
33567         this.fireEvent("refresh", this);
33568     },
33569
33570     handleColumnMove : function(cm, oldIndex, newIndex){
33571         this.indexMap = null;
33572         var s = this.getScrollState();
33573         this.refresh(true);
33574         this.restoreScroll(s);
33575         this.afterMove(newIndex);
33576     },
33577
33578     afterMove : function(colIndex){
33579         if(this.enableMoveAnim && Roo.enableFx){
33580             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33581         }
33582     },
33583
33584     updateCell : function(dm, rowIndex, dataIndex){
33585         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33586         if(typeof colIndex == "undefined"){ // not present in grid
33587             return;
33588         }
33589         var cm = this.grid.colModel;
33590         var cell = this.getCell(rowIndex, colIndex);
33591         var cellText = this.getCellText(rowIndex, colIndex);
33592
33593         var p = {
33594             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33595             id : cm.getColumnId(colIndex),
33596             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33597         };
33598         var renderer = cm.getRenderer(colIndex);
33599         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33600         if(typeof val == "undefined" || val === "") val = "&#160;";
33601         cellText.innerHTML = val;
33602         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33603         this.syncRowHeights(rowIndex, rowIndex);
33604     },
33605
33606     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33607         var maxWidth = 0;
33608         if(this.grid.autoSizeHeaders){
33609             var h = this.getHeaderCellMeasure(colIndex);
33610             maxWidth = Math.max(maxWidth, h.scrollWidth);
33611         }
33612         var tb, index;
33613         if(this.cm.isLocked(colIndex)){
33614             tb = this.getLockedTable();
33615             index = colIndex;
33616         }else{
33617             tb = this.getBodyTable();
33618             index = colIndex - this.cm.getLockedCount();
33619         }
33620         if(tb && tb.rows){
33621             var rows = tb.rows;
33622             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33623             for(var i = 0; i < stopIndex; i++){
33624                 var cell = rows[i].childNodes[index].firstChild;
33625                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33626             }
33627         }
33628         return maxWidth + /*margin for error in IE*/ 5;
33629     },
33630     /**
33631      * Autofit a column to its content.
33632      * @param {Number} colIndex
33633      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33634      */
33635      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33636          if(this.cm.isHidden(colIndex)){
33637              return; // can't calc a hidden column
33638          }
33639         if(forceMinSize){
33640             var cid = this.cm.getColumnId(colIndex);
33641             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33642            if(this.grid.autoSizeHeaders){
33643                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33644            }
33645         }
33646         var newWidth = this.calcColumnWidth(colIndex);
33647         this.cm.setColumnWidth(colIndex,
33648             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33649         if(!suppressEvent){
33650             this.grid.fireEvent("columnresize", colIndex, newWidth);
33651         }
33652     },
33653
33654     /**
33655      * Autofits all columns to their content and then expands to fit any extra space in the grid
33656      */
33657      autoSizeColumns : function(){
33658         var cm = this.grid.colModel;
33659         var colCount = cm.getColumnCount();
33660         for(var i = 0; i < colCount; i++){
33661             this.autoSizeColumn(i, true, true);
33662         }
33663         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33664             this.fitColumns();
33665         }else{
33666             this.updateColumns();
33667             this.layout();
33668         }
33669     },
33670
33671     /**
33672      * Autofits all columns to the grid's width proportionate with their current size
33673      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33674      */
33675     fitColumns : function(reserveScrollSpace){
33676         var cm = this.grid.colModel;
33677         var colCount = cm.getColumnCount();
33678         var cols = [];
33679         var width = 0;
33680         var i, w;
33681         for (i = 0; i < colCount; i++){
33682             if(!cm.isHidden(i) && !cm.isFixed(i)){
33683                 w = cm.getColumnWidth(i);
33684                 cols.push(i);
33685                 cols.push(w);
33686                 width += w;
33687             }
33688         }
33689         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33690         if(reserveScrollSpace){
33691             avail -= 17;
33692         }
33693         var frac = (avail - cm.getTotalWidth())/width;
33694         while (cols.length){
33695             w = cols.pop();
33696             i = cols.pop();
33697             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33698         }
33699         this.updateColumns();
33700         this.layout();
33701     },
33702
33703     onRowSelect : function(rowIndex){
33704         var row = this.getRowComposite(rowIndex);
33705         row.addClass("x-grid-row-selected");
33706     },
33707
33708     onRowDeselect : function(rowIndex){
33709         var row = this.getRowComposite(rowIndex);
33710         row.removeClass("x-grid-row-selected");
33711     },
33712
33713     onCellSelect : function(row, col){
33714         var cell = this.getCell(row, col);
33715         if(cell){
33716             Roo.fly(cell).addClass("x-grid-cell-selected");
33717         }
33718     },
33719
33720     onCellDeselect : function(row, col){
33721         var cell = this.getCell(row, col);
33722         if(cell){
33723             Roo.fly(cell).removeClass("x-grid-cell-selected");
33724         }
33725     },
33726
33727     updateHeaderSortState : function(){
33728         var state = this.ds.getSortState();
33729         if(!state){
33730             return;
33731         }
33732         this.sortState = state;
33733         var sortColumn = this.cm.findColumnIndex(state.field);
33734         if(sortColumn != -1){
33735             var sortDir = state.direction;
33736             var sc = this.sortClasses;
33737             var hds = this.el.select(this.headerSelector).removeClass(sc);
33738             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33739         }
33740     },
33741
33742     handleHeaderClick : function(g, index){
33743         if(this.headersDisabled){
33744             return;
33745         }
33746         var dm = g.dataSource, cm = g.colModel;
33747             if(!cm.isSortable(index)){
33748             return;
33749         }
33750             g.stopEditing();
33751         dm.sort(cm.getDataIndex(index));
33752     },
33753
33754
33755     destroy : function(){
33756         if(this.colMenu){
33757             this.colMenu.removeAll();
33758             Roo.menu.MenuMgr.unregister(this.colMenu);
33759             this.colMenu.getEl().remove();
33760             delete this.colMenu;
33761         }
33762         if(this.hmenu){
33763             this.hmenu.removeAll();
33764             Roo.menu.MenuMgr.unregister(this.hmenu);
33765             this.hmenu.getEl().remove();
33766             delete this.hmenu;
33767         }
33768         if(this.grid.enableColumnMove){
33769             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33770             if(dds){
33771                 for(var dd in dds){
33772                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33773                         var elid = dds[dd].dragElId;
33774                         dds[dd].unreg();
33775                         Roo.get(elid).remove();
33776                     } else if(dds[dd].config.isTarget){
33777                         dds[dd].proxyTop.remove();
33778                         dds[dd].proxyBottom.remove();
33779                         dds[dd].unreg();
33780                     }
33781                     if(Roo.dd.DDM.locationCache[dd]){
33782                         delete Roo.dd.DDM.locationCache[dd];
33783                     }
33784                 }
33785                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33786             }
33787         }
33788         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33789         this.bind(null, null);
33790         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33791     },
33792
33793     handleLockChange : function(){
33794         this.refresh(true);
33795     },
33796
33797     onDenyColumnLock : function(){
33798
33799     },
33800
33801     onDenyColumnHide : function(){
33802
33803     },
33804
33805     handleHdMenuClick : function(item){
33806         var index = this.hdCtxIndex;
33807         var cm = this.cm, ds = this.ds;
33808         switch(item.id){
33809             case "asc":
33810                 ds.sort(cm.getDataIndex(index), "ASC");
33811                 break;
33812             case "desc":
33813                 ds.sort(cm.getDataIndex(index), "DESC");
33814                 break;
33815             case "lock":
33816                 var lc = cm.getLockedCount();
33817                 if(cm.getColumnCount(true) <= lc+1){
33818                     this.onDenyColumnLock();
33819                     return;
33820                 }
33821                 if(lc != index){
33822                     cm.setLocked(index, true, true);
33823                     cm.moveColumn(index, lc);
33824                     this.grid.fireEvent("columnmove", index, lc);
33825                 }else{
33826                     cm.setLocked(index, true);
33827                 }
33828             break;
33829             case "unlock":
33830                 var lc = cm.getLockedCount();
33831                 if((lc-1) != index){
33832                     cm.setLocked(index, false, true);
33833                     cm.moveColumn(index, lc-1);
33834                     this.grid.fireEvent("columnmove", index, lc-1);
33835                 }else{
33836                     cm.setLocked(index, false);
33837                 }
33838             break;
33839             default:
33840                 index = cm.getIndexById(item.id.substr(4));
33841                 if(index != -1){
33842                     if(item.checked && cm.getColumnCount(true) <= 1){
33843                         this.onDenyColumnHide();
33844                         return false;
33845                     }
33846                     cm.setHidden(index, item.checked);
33847                 }
33848         }
33849         return true;
33850     },
33851
33852     beforeColMenuShow : function(){
33853         var cm = this.cm,  colCount = cm.getColumnCount();
33854         this.colMenu.removeAll();
33855         for(var i = 0; i < colCount; i++){
33856             this.colMenu.add(new Roo.menu.CheckItem({
33857                 id: "col-"+cm.getColumnId(i),
33858                 text: cm.getColumnHeader(i),
33859                 checked: !cm.isHidden(i),
33860                 hideOnClick:false
33861             }));
33862         }
33863     },
33864
33865     handleHdCtx : function(g, index, e){
33866         e.stopEvent();
33867         var hd = this.getHeaderCell(index);
33868         this.hdCtxIndex = index;
33869         var ms = this.hmenu.items, cm = this.cm;
33870         ms.get("asc").setDisabled(!cm.isSortable(index));
33871         ms.get("desc").setDisabled(!cm.isSortable(index));
33872         if(this.grid.enableColLock !== false){
33873             ms.get("lock").setDisabled(cm.isLocked(index));
33874             ms.get("unlock").setDisabled(!cm.isLocked(index));
33875         }
33876         this.hmenu.show(hd, "tl-bl");
33877     },
33878
33879     handleHdOver : function(e){
33880         var hd = this.findHeaderCell(e.getTarget());
33881         if(hd && !this.headersDisabled){
33882             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33883                this.fly(hd).addClass("x-grid-hd-over");
33884             }
33885         }
33886     },
33887
33888     handleHdOut : function(e){
33889         var hd = this.findHeaderCell(e.getTarget());
33890         if(hd){
33891             this.fly(hd).removeClass("x-grid-hd-over");
33892         }
33893     },
33894
33895     handleSplitDblClick : function(e, t){
33896         var i = this.getCellIndex(t);
33897         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33898             this.autoSizeColumn(i, true);
33899             this.layout();
33900         }
33901     },
33902
33903     render : function(){
33904
33905         var cm = this.cm;
33906         var colCount = cm.getColumnCount();
33907
33908         if(this.grid.monitorWindowResize === true){
33909             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33910         }
33911         var header = this.renderHeaders();
33912         var body = this.templates.body.apply({rows:""});
33913         var html = this.templates.master.apply({
33914             lockedBody: body,
33915             body: body,
33916             lockedHeader: header[0],
33917             header: header[1]
33918         });
33919
33920         //this.updateColumns();
33921
33922         this.grid.getGridEl().dom.innerHTML = html;
33923
33924         this.initElements();
33925         
33926         // a kludge to fix the random scolling effect in webkit
33927         this.el.on("scroll", function() {
33928             this.el.dom.scrollTop=0; // hopefully not recursive..
33929         },this);
33930
33931         this.scroller.on("scroll", this.handleScroll, this);
33932         this.lockedBody.on("mousewheel", this.handleWheel, this);
33933         this.mainBody.on("mousewheel", this.handleWheel, this);
33934
33935         this.mainHd.on("mouseover", this.handleHdOver, this);
33936         this.mainHd.on("mouseout", this.handleHdOut, this);
33937         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
33938                 {delegate: "."+this.splitClass});
33939
33940         this.lockedHd.on("mouseover", this.handleHdOver, this);
33941         this.lockedHd.on("mouseout", this.handleHdOut, this);
33942         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
33943                 {delegate: "."+this.splitClass});
33944
33945         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
33946             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33947         }
33948
33949         this.updateSplitters();
33950
33951         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
33952             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33953             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33954         }
33955
33956         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
33957             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
33958             this.hmenu.add(
33959                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
33960                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
33961             );
33962             if(this.grid.enableColLock !== false){
33963                 this.hmenu.add('-',
33964                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
33965                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
33966                 );
33967             }
33968             if(this.grid.enableColumnHide !== false){
33969
33970                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
33971                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
33972                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
33973
33974                 this.hmenu.add('-',
33975                     {id:"columns", text: this.columnsText, menu: this.colMenu}
33976                 );
33977             }
33978             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
33979
33980             this.grid.on("headercontextmenu", this.handleHdCtx, this);
33981         }
33982
33983         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
33984             this.dd = new Roo.grid.GridDragZone(this.grid, {
33985                 ddGroup : this.grid.ddGroup || 'GridDD'
33986             });
33987         }
33988
33989         /*
33990         for(var i = 0; i < colCount; i++){
33991             if(cm.isHidden(i)){
33992                 this.hideColumn(i);
33993             }
33994             if(cm.config[i].align){
33995                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
33996                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
33997             }
33998         }*/
33999         
34000         this.updateHeaderSortState();
34001
34002         this.beforeInitialResize();
34003         this.layout(true);
34004
34005         // two part rendering gives faster view to the user
34006         this.renderPhase2.defer(1, this);
34007     },
34008
34009     renderPhase2 : function(){
34010         // render the rows now
34011         this.refresh();
34012         if(this.grid.autoSizeColumns){
34013             this.autoSizeColumns();
34014         }
34015     },
34016
34017     beforeInitialResize : function(){
34018
34019     },
34020
34021     onColumnSplitterMoved : function(i, w){
34022         this.userResized = true;
34023         var cm = this.grid.colModel;
34024         cm.setColumnWidth(i, w, true);
34025         var cid = cm.getColumnId(i);
34026         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34027         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34028         this.updateSplitters();
34029         this.layout();
34030         this.grid.fireEvent("columnresize", i, w);
34031     },
34032
34033     syncRowHeights : function(startIndex, endIndex){
34034         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34035             startIndex = startIndex || 0;
34036             var mrows = this.getBodyTable().rows;
34037             var lrows = this.getLockedTable().rows;
34038             var len = mrows.length-1;
34039             endIndex = Math.min(endIndex || len, len);
34040             for(var i = startIndex; i <= endIndex; i++){
34041                 var m = mrows[i], l = lrows[i];
34042                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34043                 m.style.height = l.style.height = h + "px";
34044             }
34045         }
34046     },
34047
34048     layout : function(initialRender, is2ndPass){
34049         var g = this.grid;
34050         var auto = g.autoHeight;
34051         var scrollOffset = 16;
34052         var c = g.getGridEl(), cm = this.cm,
34053                 expandCol = g.autoExpandColumn,
34054                 gv = this;
34055         //c.beginMeasure();
34056
34057         if(!c.dom.offsetWidth){ // display:none?
34058             if(initialRender){
34059                 this.lockedWrap.show();
34060                 this.mainWrap.show();
34061             }
34062             return;
34063         }
34064
34065         var hasLock = this.cm.isLocked(0);
34066
34067         var tbh = this.headerPanel.getHeight();
34068         var bbh = this.footerPanel.getHeight();
34069
34070         if(auto){
34071             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34072             var newHeight = ch + c.getBorderWidth("tb");
34073             if(g.maxHeight){
34074                 newHeight = Math.min(g.maxHeight, newHeight);
34075             }
34076             c.setHeight(newHeight);
34077         }
34078
34079         if(g.autoWidth){
34080             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34081         }
34082
34083         var s = this.scroller;
34084
34085         var csize = c.getSize(true);
34086
34087         this.el.setSize(csize.width, csize.height);
34088
34089         this.headerPanel.setWidth(csize.width);
34090         this.footerPanel.setWidth(csize.width);
34091
34092         var hdHeight = this.mainHd.getHeight();
34093         var vw = csize.width;
34094         var vh = csize.height - (tbh + bbh);
34095
34096         s.setSize(vw, vh);
34097
34098         var bt = this.getBodyTable();
34099         var ltWidth = hasLock ?
34100                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34101
34102         var scrollHeight = bt.offsetHeight;
34103         var scrollWidth = ltWidth + bt.offsetWidth;
34104         var vscroll = false, hscroll = false;
34105
34106         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34107
34108         var lw = this.lockedWrap, mw = this.mainWrap;
34109         var lb = this.lockedBody, mb = this.mainBody;
34110
34111         setTimeout(function(){
34112             var t = s.dom.offsetTop;
34113             var w = s.dom.clientWidth,
34114                 h = s.dom.clientHeight;
34115
34116             lw.setTop(t);
34117             lw.setSize(ltWidth, h);
34118
34119             mw.setLeftTop(ltWidth, t);
34120             mw.setSize(w-ltWidth, h);
34121
34122             lb.setHeight(h-hdHeight);
34123             mb.setHeight(h-hdHeight);
34124
34125             if(is2ndPass !== true && !gv.userResized && expandCol){
34126                 // high speed resize without full column calculation
34127                 
34128                 var ci = cm.getIndexById(expandCol);
34129                 if (ci < 0) {
34130                     ci = cm.findColumnIndex(expandCol);
34131                 }
34132                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34133                 var expandId = cm.getColumnId(ci);
34134                 var  tw = cm.getTotalWidth(false);
34135                 var currentWidth = cm.getColumnWidth(ci);
34136                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34137                 if(currentWidth != cw){
34138                     cm.setColumnWidth(ci, cw, true);
34139                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34140                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34141                     gv.updateSplitters();
34142                     gv.layout(false, true);
34143                 }
34144             }
34145
34146             if(initialRender){
34147                 lw.show();
34148                 mw.show();
34149             }
34150             //c.endMeasure();
34151         }, 10);
34152     },
34153
34154     onWindowResize : function(){
34155         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34156             return;
34157         }
34158         this.layout();
34159     },
34160
34161     appendFooter : function(parentEl){
34162         return null;
34163     },
34164
34165     sortAscText : "Sort Ascending",
34166     sortDescText : "Sort Descending",
34167     lockText : "Lock Column",
34168     unlockText : "Unlock Column",
34169     columnsText : "Columns"
34170 });
34171
34172
34173 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34174     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34175     this.proxy.el.addClass('x-grid3-col-dd');
34176 };
34177
34178 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34179     handleMouseDown : function(e){
34180
34181     },
34182
34183     callHandleMouseDown : function(e){
34184         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34185     }
34186 });
34187 /*
34188  * Based on:
34189  * Ext JS Library 1.1.1
34190  * Copyright(c) 2006-2007, Ext JS, LLC.
34191  *
34192  * Originally Released Under LGPL - original licence link has changed is not relivant.
34193  *
34194  * Fork - LGPL
34195  * <script type="text/javascript">
34196  */
34197  
34198 // private
34199 // This is a support class used internally by the Grid components
34200 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34201     this.grid = grid;
34202     this.view = grid.getView();
34203     this.proxy = this.view.resizeProxy;
34204     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34205         "gridSplitters" + this.grid.getGridEl().id, {
34206         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34207     });
34208     this.setHandleElId(Roo.id(hd));
34209     this.setOuterHandleElId(Roo.id(hd2));
34210     this.scroll = false;
34211 };
34212 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34213     fly: Roo.Element.fly,
34214
34215     b4StartDrag : function(x, y){
34216         this.view.headersDisabled = true;
34217         this.proxy.setHeight(this.view.mainWrap.getHeight());
34218         var w = this.cm.getColumnWidth(this.cellIndex);
34219         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34220         this.resetConstraints();
34221         this.setXConstraint(minw, 1000);
34222         this.setYConstraint(0, 0);
34223         this.minX = x - minw;
34224         this.maxX = x + 1000;
34225         this.startPos = x;
34226         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34227     },
34228
34229
34230     handleMouseDown : function(e){
34231         ev = Roo.EventObject.setEvent(e);
34232         var t = this.fly(ev.getTarget());
34233         if(t.hasClass("x-grid-split")){
34234             this.cellIndex = this.view.getCellIndex(t.dom);
34235             this.split = t.dom;
34236             this.cm = this.grid.colModel;
34237             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34238                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34239             }
34240         }
34241     },
34242
34243     endDrag : function(e){
34244         this.view.headersDisabled = false;
34245         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34246         var diff = endX - this.startPos;
34247         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34248     },
34249
34250     autoOffset : function(){
34251         this.setDelta(0,0);
34252     }
34253 });/*
34254  * Based on:
34255  * Ext JS Library 1.1.1
34256  * Copyright(c) 2006-2007, Ext JS, LLC.
34257  *
34258  * Originally Released Under LGPL - original licence link has changed is not relivant.
34259  *
34260  * Fork - LGPL
34261  * <script type="text/javascript">
34262  */
34263  
34264 // private
34265 // This is a support class used internally by the Grid components
34266 Roo.grid.GridDragZone = function(grid, config){
34267     this.view = grid.getView();
34268     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34269     if(this.view.lockedBody){
34270         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34271         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34272     }
34273     this.scroll = false;
34274     this.grid = grid;
34275     this.ddel = document.createElement('div');
34276     this.ddel.className = 'x-grid-dd-wrap';
34277 };
34278
34279 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34280     ddGroup : "GridDD",
34281
34282     getDragData : function(e){
34283         var t = Roo.lib.Event.getTarget(e);
34284         var rowIndex = this.view.findRowIndex(t);
34285         if(rowIndex !== false){
34286             var sm = this.grid.selModel;
34287             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34288               //  sm.mouseDown(e, t);
34289             //}
34290             if (e.hasModifier()){
34291                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34292             }
34293             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34294         }
34295         return false;
34296     },
34297
34298     onInitDrag : function(e){
34299         var data = this.dragData;
34300         this.ddel.innerHTML = this.grid.getDragDropText();
34301         this.proxy.update(this.ddel);
34302         // fire start drag?
34303     },
34304
34305     afterRepair : function(){
34306         this.dragging = false;
34307     },
34308
34309     getRepairXY : function(e, data){
34310         return false;
34311     },
34312
34313     onEndDrag : function(data, e){
34314         // fire end drag?
34315     },
34316
34317     onValidDrop : function(dd, e, id){
34318         // fire drag drop?
34319         this.hideProxy();
34320     },
34321
34322     beforeInvalidDrop : function(e, id){
34323
34324     }
34325 });/*
34326  * Based on:
34327  * Ext JS Library 1.1.1
34328  * Copyright(c) 2006-2007, Ext JS, LLC.
34329  *
34330  * Originally Released Under LGPL - original licence link has changed is not relivant.
34331  *
34332  * Fork - LGPL
34333  * <script type="text/javascript">
34334  */
34335  
34336
34337 /**
34338  * @class Roo.grid.ColumnModel
34339  * @extends Roo.util.Observable
34340  * This is the default implementation of a ColumnModel used by the Grid. It defines
34341  * the columns in the grid.
34342  * <br>Usage:<br>
34343  <pre><code>
34344  var colModel = new Roo.grid.ColumnModel([
34345         {header: "Ticker", width: 60, sortable: true, locked: true},
34346         {header: "Company Name", width: 150, sortable: true},
34347         {header: "Market Cap.", width: 100, sortable: true},
34348         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34349         {header: "Employees", width: 100, sortable: true, resizable: false}
34350  ]);
34351  </code></pre>
34352  * <p>
34353  
34354  * The config options listed for this class are options which may appear in each
34355  * individual column definition.
34356  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34357  * @constructor
34358  * @param {Object} config An Array of column config objects. See this class's
34359  * config objects for details.
34360 */
34361 Roo.grid.ColumnModel = function(config){
34362         /**
34363      * The config passed into the constructor
34364      */
34365     this.config = config;
34366     this.lookup = {};
34367
34368     // if no id, create one
34369     // if the column does not have a dataIndex mapping,
34370     // map it to the order it is in the config
34371     for(var i = 0, len = config.length; i < len; i++){
34372         var c = config[i];
34373         if(typeof c.dataIndex == "undefined"){
34374             c.dataIndex = i;
34375         }
34376         if(typeof c.renderer == "string"){
34377             c.renderer = Roo.util.Format[c.renderer];
34378         }
34379         if(typeof c.id == "undefined"){
34380             c.id = Roo.id();
34381         }
34382         if(c.editor && c.editor.xtype){
34383             c.editor  = Roo.factory(c.editor, Roo.grid);
34384         }
34385         if(c.editor && c.editor.isFormField){
34386             c.editor = new Roo.grid.GridEditor(c.editor);
34387         }
34388         this.lookup[c.id] = c;
34389     }
34390
34391     /**
34392      * The width of columns which have no width specified (defaults to 100)
34393      * @type Number
34394      */
34395     this.defaultWidth = 100;
34396
34397     /**
34398      * Default sortable of columns which have no sortable specified (defaults to false)
34399      * @type Boolean
34400      */
34401     this.defaultSortable = false;
34402
34403     this.addEvents({
34404         /**
34405              * @event widthchange
34406              * Fires when the width of a column changes.
34407              * @param {ColumnModel} this
34408              * @param {Number} columnIndex The column index
34409              * @param {Number} newWidth The new width
34410              */
34411             "widthchange": true,
34412         /**
34413              * @event headerchange
34414              * Fires when the text of a header changes.
34415              * @param {ColumnModel} this
34416              * @param {Number} columnIndex The column index
34417              * @param {Number} newText The new header text
34418              */
34419             "headerchange": true,
34420         /**
34421              * @event hiddenchange
34422              * Fires when a column is hidden or "unhidden".
34423              * @param {ColumnModel} this
34424              * @param {Number} columnIndex The column index
34425              * @param {Boolean} hidden true if hidden, false otherwise
34426              */
34427             "hiddenchange": true,
34428             /**
34429          * @event columnmoved
34430          * Fires when a column is moved.
34431          * @param {ColumnModel} this
34432          * @param {Number} oldIndex
34433          * @param {Number} newIndex
34434          */
34435         "columnmoved" : true,
34436         /**
34437          * @event columlockchange
34438          * Fires when a column's locked state is changed
34439          * @param {ColumnModel} this
34440          * @param {Number} colIndex
34441          * @param {Boolean} locked true if locked
34442          */
34443         "columnlockchange" : true
34444     });
34445     Roo.grid.ColumnModel.superclass.constructor.call(this);
34446 };
34447 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34448     /**
34449      * @cfg {String} header The header text to display in the Grid view.
34450      */
34451     /**
34452      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34453      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34454      * specified, the column's index is used as an index into the Record's data Array.
34455      */
34456     /**
34457      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34458      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34459      */
34460     /**
34461      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34462      * Defaults to the value of the {@link #defaultSortable} property.
34463      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34464      */
34465     /**
34466      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34467      */
34468     /**
34469      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34470      */
34471     /**
34472      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34473      */
34474     /**
34475      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34476      */
34477     /**
34478      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34479      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34480      * default renderer uses the raw data value.
34481      */
34482        /**
34483      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34484      */
34485     /**
34486      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34487      */
34488
34489     /**
34490      * Returns the id of the column at the specified index.
34491      * @param {Number} index The column index
34492      * @return {String} the id
34493      */
34494     getColumnId : function(index){
34495         return this.config[index].id;
34496     },
34497
34498     /**
34499      * Returns the column for a specified id.
34500      * @param {String} id The column id
34501      * @return {Object} the column
34502      */
34503     getColumnById : function(id){
34504         return this.lookup[id];
34505     },
34506
34507     
34508     /**
34509      * Returns the column for a specified dataIndex.
34510      * @param {String} dataIndex The column dataIndex
34511      * @return {Object|Boolean} the column or false if not found
34512      */
34513     getColumnByDataIndex: function(dataIndex){
34514         var index = this.findColumnIndex(dataIndex);
34515         return index > -1 ? this.config[index] : false;
34516     },
34517     
34518     /**
34519      * Returns the index for a specified column id.
34520      * @param {String} id The column id
34521      * @return {Number} the index, or -1 if not found
34522      */
34523     getIndexById : function(id){
34524         for(var i = 0, len = this.config.length; i < len; i++){
34525             if(this.config[i].id == id){
34526                 return i;
34527             }
34528         }
34529         return -1;
34530     },
34531     
34532     /**
34533      * Returns the index for a specified column dataIndex.
34534      * @param {String} dataIndex The column dataIndex
34535      * @return {Number} the index, or -1 if not found
34536      */
34537     
34538     findColumnIndex : function(dataIndex){
34539         for(var i = 0, len = this.config.length; i < len; i++){
34540             if(this.config[i].dataIndex == dataIndex){
34541                 return i;
34542             }
34543         }
34544         return -1;
34545     },
34546     
34547     
34548     moveColumn : function(oldIndex, newIndex){
34549         var c = this.config[oldIndex];
34550         this.config.splice(oldIndex, 1);
34551         this.config.splice(newIndex, 0, c);
34552         this.dataMap = null;
34553         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34554     },
34555
34556     isLocked : function(colIndex){
34557         return this.config[colIndex].locked === true;
34558     },
34559
34560     setLocked : function(colIndex, value, suppressEvent){
34561         if(this.isLocked(colIndex) == value){
34562             return;
34563         }
34564         this.config[colIndex].locked = value;
34565         if(!suppressEvent){
34566             this.fireEvent("columnlockchange", this, colIndex, value);
34567         }
34568     },
34569
34570     getTotalLockedWidth : function(){
34571         var totalWidth = 0;
34572         for(var i = 0; i < this.config.length; i++){
34573             if(this.isLocked(i) && !this.isHidden(i)){
34574                 this.totalWidth += this.getColumnWidth(i);
34575             }
34576         }
34577         return totalWidth;
34578     },
34579
34580     getLockedCount : function(){
34581         for(var i = 0, len = this.config.length; i < len; i++){
34582             if(!this.isLocked(i)){
34583                 return i;
34584             }
34585         }
34586     },
34587
34588     /**
34589      * Returns the number of columns.
34590      * @return {Number}
34591      */
34592     getColumnCount : function(visibleOnly){
34593         if(visibleOnly === true){
34594             var c = 0;
34595             for(var i = 0, len = this.config.length; i < len; i++){
34596                 if(!this.isHidden(i)){
34597                     c++;
34598                 }
34599             }
34600             return c;
34601         }
34602         return this.config.length;
34603     },
34604
34605     /**
34606      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34607      * @param {Function} fn
34608      * @param {Object} scope (optional)
34609      * @return {Array} result
34610      */
34611     getColumnsBy : function(fn, scope){
34612         var r = [];
34613         for(var i = 0, len = this.config.length; i < len; i++){
34614             var c = this.config[i];
34615             if(fn.call(scope||this, c, i) === true){
34616                 r[r.length] = c;
34617             }
34618         }
34619         return r;
34620     },
34621
34622     /**
34623      * Returns true if the specified column is sortable.
34624      * @param {Number} col The column index
34625      * @return {Boolean}
34626      */
34627     isSortable : function(col){
34628         if(typeof this.config[col].sortable == "undefined"){
34629             return this.defaultSortable;
34630         }
34631         return this.config[col].sortable;
34632     },
34633
34634     /**
34635      * Returns the rendering (formatting) function defined for the column.
34636      * @param {Number} col The column index.
34637      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34638      */
34639     getRenderer : function(col){
34640         if(!this.config[col].renderer){
34641             return Roo.grid.ColumnModel.defaultRenderer;
34642         }
34643         return this.config[col].renderer;
34644     },
34645
34646     /**
34647      * Sets the rendering (formatting) function for a column.
34648      * @param {Number} col The column index
34649      * @param {Function} fn The function to use to process the cell's raw data
34650      * to return HTML markup for the grid view. The render function is called with
34651      * the following parameters:<ul>
34652      * <li>Data value.</li>
34653      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34654      * <li>css A CSS style string to apply to the table cell.</li>
34655      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34656      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34657      * <li>Row index</li>
34658      * <li>Column index</li>
34659      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34660      */
34661     setRenderer : function(col, fn){
34662         this.config[col].renderer = fn;
34663     },
34664
34665     /**
34666      * Returns the width for the specified column.
34667      * @param {Number} col The column index
34668      * @return {Number}
34669      */
34670     getColumnWidth : function(col){
34671         return this.config[col].width || this.defaultWidth;
34672     },
34673
34674     /**
34675      * Sets the width for a column.
34676      * @param {Number} col The column index
34677      * @param {Number} width The new width
34678      */
34679     setColumnWidth : function(col, width, suppressEvent){
34680         this.config[col].width = width;
34681         this.totalWidth = null;
34682         if(!suppressEvent){
34683              this.fireEvent("widthchange", this, col, width);
34684         }
34685     },
34686
34687     /**
34688      * Returns the total width of all columns.
34689      * @param {Boolean} includeHidden True to include hidden column widths
34690      * @return {Number}
34691      */
34692     getTotalWidth : function(includeHidden){
34693         if(!this.totalWidth){
34694             this.totalWidth = 0;
34695             for(var i = 0, len = this.config.length; i < len; i++){
34696                 if(includeHidden || !this.isHidden(i)){
34697                     this.totalWidth += this.getColumnWidth(i);
34698                 }
34699             }
34700         }
34701         return this.totalWidth;
34702     },
34703
34704     /**
34705      * Returns the header for the specified column.
34706      * @param {Number} col The column index
34707      * @return {String}
34708      */
34709     getColumnHeader : function(col){
34710         return this.config[col].header;
34711     },
34712
34713     /**
34714      * Sets the header for a column.
34715      * @param {Number} col The column index
34716      * @param {String} header The new header
34717      */
34718     setColumnHeader : function(col, header){
34719         this.config[col].header = header;
34720         this.fireEvent("headerchange", this, col, header);
34721     },
34722
34723     /**
34724      * Returns the tooltip for the specified column.
34725      * @param {Number} col The column index
34726      * @return {String}
34727      */
34728     getColumnTooltip : function(col){
34729             return this.config[col].tooltip;
34730     },
34731     /**
34732      * Sets the tooltip for a column.
34733      * @param {Number} col The column index
34734      * @param {String} tooltip The new tooltip
34735      */
34736     setColumnTooltip : function(col, tooltip){
34737             this.config[col].tooltip = tooltip;
34738     },
34739
34740     /**
34741      * Returns the dataIndex for the specified column.
34742      * @param {Number} col The column index
34743      * @return {Number}
34744      */
34745     getDataIndex : function(col){
34746         return this.config[col].dataIndex;
34747     },
34748
34749     /**
34750      * Sets the dataIndex for a column.
34751      * @param {Number} col The column index
34752      * @param {Number} dataIndex The new dataIndex
34753      */
34754     setDataIndex : function(col, dataIndex){
34755         this.config[col].dataIndex = dataIndex;
34756     },
34757
34758     
34759     
34760     /**
34761      * Returns true if the cell is editable.
34762      * @param {Number} colIndex The column index
34763      * @param {Number} rowIndex The row index
34764      * @return {Boolean}
34765      */
34766     isCellEditable : function(colIndex, rowIndex){
34767         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34768     },
34769
34770     /**
34771      * Returns the editor defined for the cell/column.
34772      * return false or null to disable editing.
34773      * @param {Number} colIndex The column index
34774      * @param {Number} rowIndex The row index
34775      * @return {Object}
34776      */
34777     getCellEditor : function(colIndex, rowIndex){
34778         return this.config[colIndex].editor;
34779     },
34780
34781     /**
34782      * Sets if a column is editable.
34783      * @param {Number} col The column index
34784      * @param {Boolean} editable True if the column is editable
34785      */
34786     setEditable : function(col, editable){
34787         this.config[col].editable = editable;
34788     },
34789
34790
34791     /**
34792      * Returns true if the column is hidden.
34793      * @param {Number} colIndex The column index
34794      * @return {Boolean}
34795      */
34796     isHidden : function(colIndex){
34797         return this.config[colIndex].hidden;
34798     },
34799
34800
34801     /**
34802      * Returns true if the column width cannot be changed
34803      */
34804     isFixed : function(colIndex){
34805         return this.config[colIndex].fixed;
34806     },
34807
34808     /**
34809      * Returns true if the column can be resized
34810      * @return {Boolean}
34811      */
34812     isResizable : function(colIndex){
34813         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34814     },
34815     /**
34816      * Sets if a column is hidden.
34817      * @param {Number} colIndex The column index
34818      * @param {Boolean} hidden True if the column is hidden
34819      */
34820     setHidden : function(colIndex, hidden){
34821         this.config[colIndex].hidden = hidden;
34822         this.totalWidth = null;
34823         this.fireEvent("hiddenchange", this, colIndex, hidden);
34824     },
34825
34826     /**
34827      * Sets the editor for a column.
34828      * @param {Number} col The column index
34829      * @param {Object} editor The editor object
34830      */
34831     setEditor : function(col, editor){
34832         this.config[col].editor = editor;
34833     }
34834 });
34835
34836 Roo.grid.ColumnModel.defaultRenderer = function(value){
34837         if(typeof value == "string" && value.length < 1){
34838             return "&#160;";
34839         }
34840         return value;
34841 };
34842
34843 // Alias for backwards compatibility
34844 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34845 /*
34846  * Based on:
34847  * Ext JS Library 1.1.1
34848  * Copyright(c) 2006-2007, Ext JS, LLC.
34849  *
34850  * Originally Released Under LGPL - original licence link has changed is not relivant.
34851  *
34852  * Fork - LGPL
34853  * <script type="text/javascript">
34854  */
34855
34856 /**
34857  * @class Roo.grid.AbstractSelectionModel
34858  * @extends Roo.util.Observable
34859  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34860  * implemented by descendant classes.  This class should not be directly instantiated.
34861  * @constructor
34862  */
34863 Roo.grid.AbstractSelectionModel = function(){
34864     this.locked = false;
34865     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34866 };
34867
34868 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34869     /** @ignore Called by the grid automatically. Do not call directly. */
34870     init : function(grid){
34871         this.grid = grid;
34872         this.initEvents();
34873     },
34874
34875     /**
34876      * Locks the selections.
34877      */
34878     lock : function(){
34879         this.locked = true;
34880     },
34881
34882     /**
34883      * Unlocks the selections.
34884      */
34885     unlock : function(){
34886         this.locked = false;
34887     },
34888
34889     /**
34890      * Returns true if the selections are locked.
34891      * @return {Boolean}
34892      */
34893     isLocked : function(){
34894         return this.locked;
34895     }
34896 });/*
34897  * Based on:
34898  * Ext JS Library 1.1.1
34899  * Copyright(c) 2006-2007, Ext JS, LLC.
34900  *
34901  * Originally Released Under LGPL - original licence link has changed is not relivant.
34902  *
34903  * Fork - LGPL
34904  * <script type="text/javascript">
34905  */
34906 /**
34907  * @extends Roo.grid.AbstractSelectionModel
34908  * @class Roo.grid.RowSelectionModel
34909  * The default SelectionModel used by {@link Roo.grid.Grid}.
34910  * It supports multiple selections and keyboard selection/navigation. 
34911  * @constructor
34912  * @param {Object} config
34913  */
34914 Roo.grid.RowSelectionModel = function(config){
34915     Roo.apply(this, config);
34916     this.selections = new Roo.util.MixedCollection(false, function(o){
34917         return o.id;
34918     });
34919
34920     this.last = false;
34921     this.lastActive = false;
34922
34923     this.addEvents({
34924         /**
34925              * @event selectionchange
34926              * Fires when the selection changes
34927              * @param {SelectionModel} this
34928              */
34929             "selectionchange" : true,
34930         /**
34931              * @event afterselectionchange
34932              * Fires after the selection changes (eg. by key press or clicking)
34933              * @param {SelectionModel} this
34934              */
34935             "afterselectionchange" : true,
34936         /**
34937              * @event beforerowselect
34938              * Fires when a row is selected being selected, return false to cancel.
34939              * @param {SelectionModel} this
34940              * @param {Number} rowIndex The selected index
34941              * @param {Boolean} keepExisting False if other selections will be cleared
34942              */
34943             "beforerowselect" : true,
34944         /**
34945              * @event rowselect
34946              * Fires when a row is selected.
34947              * @param {SelectionModel} this
34948              * @param {Number} rowIndex The selected index
34949              * @param {Roo.data.Record} r The record
34950              */
34951             "rowselect" : true,
34952         /**
34953              * @event rowdeselect
34954              * Fires when a row is deselected.
34955              * @param {SelectionModel} this
34956              * @param {Number} rowIndex The selected index
34957              */
34958         "rowdeselect" : true
34959     });
34960     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
34961     this.locked = false;
34962 };
34963
34964 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
34965     /**
34966      * @cfg {Boolean} singleSelect
34967      * True to allow selection of only one row at a time (defaults to false)
34968      */
34969     singleSelect : false,
34970
34971     // private
34972     initEvents : function(){
34973
34974         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
34975             this.grid.on("mousedown", this.handleMouseDown, this);
34976         }else{ // allow click to work like normal
34977             this.grid.on("rowclick", this.handleDragableRowClick, this);
34978         }
34979
34980         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
34981             "up" : function(e){
34982                 if(!e.shiftKey){
34983                     this.selectPrevious(e.shiftKey);
34984                 }else if(this.last !== false && this.lastActive !== false){
34985                     var last = this.last;
34986                     this.selectRange(this.last,  this.lastActive-1);
34987                     this.grid.getView().focusRow(this.lastActive);
34988                     if(last !== false){
34989                         this.last = last;
34990                     }
34991                 }else{
34992                     this.selectFirstRow();
34993                 }
34994                 this.fireEvent("afterselectionchange", this);
34995             },
34996             "down" : function(e){
34997                 if(!e.shiftKey){
34998                     this.selectNext(e.shiftKey);
34999                 }else if(this.last !== false && this.lastActive !== false){
35000                     var last = this.last;
35001                     this.selectRange(this.last,  this.lastActive+1);
35002                     this.grid.getView().focusRow(this.lastActive);
35003                     if(last !== false){
35004                         this.last = last;
35005                     }
35006                 }else{
35007                     this.selectFirstRow();
35008                 }
35009                 this.fireEvent("afterselectionchange", this);
35010             },
35011             scope: this
35012         });
35013
35014         var view = this.grid.view;
35015         view.on("refresh", this.onRefresh, this);
35016         view.on("rowupdated", this.onRowUpdated, this);
35017         view.on("rowremoved", this.onRemove, this);
35018     },
35019
35020     // private
35021     onRefresh : function(){
35022         var ds = this.grid.dataSource, i, v = this.grid.view;
35023         var s = this.selections;
35024         s.each(function(r){
35025             if((i = ds.indexOfId(r.id)) != -1){
35026                 v.onRowSelect(i);
35027             }else{
35028                 s.remove(r);
35029             }
35030         });
35031     },
35032
35033     // private
35034     onRemove : function(v, index, r){
35035         this.selections.remove(r);
35036     },
35037
35038     // private
35039     onRowUpdated : function(v, index, r){
35040         if(this.isSelected(r)){
35041             v.onRowSelect(index);
35042         }
35043     },
35044
35045     /**
35046      * Select records.
35047      * @param {Array} records The records to select
35048      * @param {Boolean} keepExisting (optional) True to keep existing selections
35049      */
35050     selectRecords : function(records, keepExisting){
35051         if(!keepExisting){
35052             this.clearSelections();
35053         }
35054         var ds = this.grid.dataSource;
35055         for(var i = 0, len = records.length; i < len; i++){
35056             this.selectRow(ds.indexOf(records[i]), true);
35057         }
35058     },
35059
35060     /**
35061      * Gets the number of selected rows.
35062      * @return {Number}
35063      */
35064     getCount : function(){
35065         return this.selections.length;
35066     },
35067
35068     /**
35069      * Selects the first row in the grid.
35070      */
35071     selectFirstRow : function(){
35072         this.selectRow(0);
35073     },
35074
35075     /**
35076      * Select the last row.
35077      * @param {Boolean} keepExisting (optional) True to keep existing selections
35078      */
35079     selectLastRow : function(keepExisting){
35080         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35081     },
35082
35083     /**
35084      * Selects the row immediately following the last selected row.
35085      * @param {Boolean} keepExisting (optional) True to keep existing selections
35086      */
35087     selectNext : function(keepExisting){
35088         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35089             this.selectRow(this.last+1, keepExisting);
35090             this.grid.getView().focusRow(this.last);
35091         }
35092     },
35093
35094     /**
35095      * Selects the row that precedes the last selected row.
35096      * @param {Boolean} keepExisting (optional) True to keep existing selections
35097      */
35098     selectPrevious : function(keepExisting){
35099         if(this.last){
35100             this.selectRow(this.last-1, keepExisting);
35101             this.grid.getView().focusRow(this.last);
35102         }
35103     },
35104
35105     /**
35106      * Returns the selected records
35107      * @return {Array} Array of selected records
35108      */
35109     getSelections : function(){
35110         return [].concat(this.selections.items);
35111     },
35112
35113     /**
35114      * Returns the first selected record.
35115      * @return {Record}
35116      */
35117     getSelected : function(){
35118         return this.selections.itemAt(0);
35119     },
35120
35121
35122     /**
35123      * Clears all selections.
35124      */
35125     clearSelections : function(fast){
35126         if(this.locked) return;
35127         if(fast !== true){
35128             var ds = this.grid.dataSource;
35129             var s = this.selections;
35130             s.each(function(r){
35131                 this.deselectRow(ds.indexOfId(r.id));
35132             }, this);
35133             s.clear();
35134         }else{
35135             this.selections.clear();
35136         }
35137         this.last = false;
35138     },
35139
35140
35141     /**
35142      * Selects all rows.
35143      */
35144     selectAll : function(){
35145         if(this.locked) return;
35146         this.selections.clear();
35147         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35148             this.selectRow(i, true);
35149         }
35150     },
35151
35152     /**
35153      * Returns True if there is a selection.
35154      * @return {Boolean}
35155      */
35156     hasSelection : function(){
35157         return this.selections.length > 0;
35158     },
35159
35160     /**
35161      * Returns True if the specified row is selected.
35162      * @param {Number/Record} record The record or index of the record to check
35163      * @return {Boolean}
35164      */
35165     isSelected : function(index){
35166         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35167         return (r && this.selections.key(r.id) ? true : false);
35168     },
35169
35170     /**
35171      * Returns True if the specified record id is selected.
35172      * @param {String} id The id of record to check
35173      * @return {Boolean}
35174      */
35175     isIdSelected : function(id){
35176         return (this.selections.key(id) ? true : false);
35177     },
35178
35179     // private
35180     handleMouseDown : function(e, t){
35181         var view = this.grid.getView(), rowIndex;
35182         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35183             return;
35184         };
35185         if(e.shiftKey && this.last !== false){
35186             var last = this.last;
35187             this.selectRange(last, rowIndex, e.ctrlKey);
35188             this.last = last; // reset the last
35189             view.focusRow(rowIndex);
35190         }else{
35191             var isSelected = this.isSelected(rowIndex);
35192             if(e.button !== 0 && isSelected){
35193                 view.focusRow(rowIndex);
35194             }else if(e.ctrlKey && isSelected){
35195                 this.deselectRow(rowIndex);
35196             }else if(!isSelected){
35197                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35198                 view.focusRow(rowIndex);
35199             }
35200         }
35201         this.fireEvent("afterselectionchange", this);
35202     },
35203     // private
35204     handleDragableRowClick :  function(grid, rowIndex, e) 
35205     {
35206         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35207             this.selectRow(rowIndex, false);
35208             grid.view.focusRow(rowIndex);
35209              this.fireEvent("afterselectionchange", this);
35210         }
35211     },
35212     
35213     /**
35214      * Selects multiple rows.
35215      * @param {Array} rows Array of the indexes of the row to select
35216      * @param {Boolean} keepExisting (optional) True to keep existing selections
35217      */
35218     selectRows : function(rows, keepExisting){
35219         if(!keepExisting){
35220             this.clearSelections();
35221         }
35222         for(var i = 0, len = rows.length; i < len; i++){
35223             this.selectRow(rows[i], true);
35224         }
35225     },
35226
35227     /**
35228      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35229      * @param {Number} startRow The index of the first row in the range
35230      * @param {Number} endRow The index of the last row in the range
35231      * @param {Boolean} keepExisting (optional) True to retain existing selections
35232      */
35233     selectRange : function(startRow, endRow, keepExisting){
35234         if(this.locked) return;
35235         if(!keepExisting){
35236             this.clearSelections();
35237         }
35238         if(startRow <= endRow){
35239             for(var i = startRow; i <= endRow; i++){
35240                 this.selectRow(i, true);
35241             }
35242         }else{
35243             for(var i = startRow; i >= endRow; i--){
35244                 this.selectRow(i, true);
35245             }
35246         }
35247     },
35248
35249     /**
35250      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35251      * @param {Number} startRow The index of the first row in the range
35252      * @param {Number} endRow The index of the last row in the range
35253      */
35254     deselectRange : function(startRow, endRow, preventViewNotify){
35255         if(this.locked) return;
35256         for(var i = startRow; i <= endRow; i++){
35257             this.deselectRow(i, preventViewNotify);
35258         }
35259     },
35260
35261     /**
35262      * Selects a row.
35263      * @param {Number} row The index of the row to select
35264      * @param {Boolean} keepExisting (optional) True to keep existing selections
35265      */
35266     selectRow : function(index, keepExisting, preventViewNotify){
35267         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35268         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35269             if(!keepExisting || this.singleSelect){
35270                 this.clearSelections();
35271             }
35272             var r = this.grid.dataSource.getAt(index);
35273             this.selections.add(r);
35274             this.last = this.lastActive = index;
35275             if(!preventViewNotify){
35276                 this.grid.getView().onRowSelect(index);
35277             }
35278             this.fireEvent("rowselect", this, index, r);
35279             this.fireEvent("selectionchange", this);
35280         }
35281     },
35282
35283     /**
35284      * Deselects a row.
35285      * @param {Number} row The index of the row to deselect
35286      */
35287     deselectRow : function(index, preventViewNotify){
35288         if(this.locked) return;
35289         if(this.last == index){
35290             this.last = false;
35291         }
35292         if(this.lastActive == index){
35293             this.lastActive = false;
35294         }
35295         var r = this.grid.dataSource.getAt(index);
35296         this.selections.remove(r);
35297         if(!preventViewNotify){
35298             this.grid.getView().onRowDeselect(index);
35299         }
35300         this.fireEvent("rowdeselect", this, index);
35301         this.fireEvent("selectionchange", this);
35302     },
35303
35304     // private
35305     restoreLast : function(){
35306         if(this._last){
35307             this.last = this._last;
35308         }
35309     },
35310
35311     // private
35312     acceptsNav : function(row, col, cm){
35313         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35314     },
35315
35316     // private
35317     onEditorKey : function(field, e){
35318         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35319         if(k == e.TAB){
35320             e.stopEvent();
35321             ed.completeEdit();
35322             if(e.shiftKey){
35323                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35324             }else{
35325                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35326             }
35327         }else if(k == e.ENTER && !e.ctrlKey){
35328             e.stopEvent();
35329             ed.completeEdit();
35330             if(e.shiftKey){
35331                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35332             }else{
35333                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35334             }
35335         }else if(k == e.ESC){
35336             ed.cancelEdit();
35337         }
35338         if(newCell){
35339             g.startEditing(newCell[0], newCell[1]);
35340         }
35341     }
35342 });/*
35343  * Based on:
35344  * Ext JS Library 1.1.1
35345  * Copyright(c) 2006-2007, Ext JS, LLC.
35346  *
35347  * Originally Released Under LGPL - original licence link has changed is not relivant.
35348  *
35349  * Fork - LGPL
35350  * <script type="text/javascript">
35351  */
35352 /**
35353  * @class Roo.grid.CellSelectionModel
35354  * @extends Roo.grid.AbstractSelectionModel
35355  * This class provides the basic implementation for cell selection in a grid.
35356  * @constructor
35357  * @param {Object} config The object containing the configuration of this model.
35358  */
35359 Roo.grid.CellSelectionModel = function(config){
35360     Roo.apply(this, config);
35361
35362     this.selection = null;
35363
35364     this.addEvents({
35365         /**
35366              * @event beforerowselect
35367              * Fires before a cell is selected.
35368              * @param {SelectionModel} this
35369              * @param {Number} rowIndex The selected row index
35370              * @param {Number} colIndex The selected cell index
35371              */
35372             "beforecellselect" : true,
35373         /**
35374              * @event cellselect
35375              * Fires when a cell is selected.
35376              * @param {SelectionModel} this
35377              * @param {Number} rowIndex The selected row index
35378              * @param {Number} colIndex The selected cell index
35379              */
35380             "cellselect" : true,
35381         /**
35382              * @event selectionchange
35383              * Fires when the active selection changes.
35384              * @param {SelectionModel} this
35385              * @param {Object} selection null for no selection or an object (o) with two properties
35386                 <ul>
35387                 <li>o.record: the record object for the row the selection is in</li>
35388                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35389                 </ul>
35390              */
35391             "selectionchange" : true
35392     });
35393     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35394 };
35395
35396 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35397
35398     /** @ignore */
35399     initEvents : function(){
35400         this.grid.on("mousedown", this.handleMouseDown, this);
35401         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35402         var view = this.grid.view;
35403         view.on("refresh", this.onViewChange, this);
35404         view.on("rowupdated", this.onRowUpdated, this);
35405         view.on("beforerowremoved", this.clearSelections, this);
35406         view.on("beforerowsinserted", this.clearSelections, this);
35407         if(this.grid.isEditor){
35408             this.grid.on("beforeedit", this.beforeEdit,  this);
35409         }
35410     },
35411
35412         //private
35413     beforeEdit : function(e){
35414         this.select(e.row, e.column, false, true, e.record);
35415     },
35416
35417         //private
35418     onRowUpdated : function(v, index, r){
35419         if(this.selection && this.selection.record == r){
35420             v.onCellSelect(index, this.selection.cell[1]);
35421         }
35422     },
35423
35424         //private
35425     onViewChange : function(){
35426         this.clearSelections(true);
35427     },
35428
35429         /**
35430          * Returns the currently selected cell,.
35431          * @return {Array} The selected cell (row, column) or null if none selected.
35432          */
35433     getSelectedCell : function(){
35434         return this.selection ? this.selection.cell : null;
35435     },
35436
35437     /**
35438      * Clears all selections.
35439      * @param {Boolean} true to prevent the gridview from being notified about the change.
35440      */
35441     clearSelections : function(preventNotify){
35442         var s = this.selection;
35443         if(s){
35444             if(preventNotify !== true){
35445                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35446             }
35447             this.selection = null;
35448             this.fireEvent("selectionchange", this, null);
35449         }
35450     },
35451
35452     /**
35453      * Returns true if there is a selection.
35454      * @return {Boolean}
35455      */
35456     hasSelection : function(){
35457         return this.selection ? true : false;
35458     },
35459
35460     /** @ignore */
35461     handleMouseDown : function(e, t){
35462         var v = this.grid.getView();
35463         if(this.isLocked()){
35464             return;
35465         };
35466         var row = v.findRowIndex(t);
35467         var cell = v.findCellIndex(t);
35468         if(row !== false && cell !== false){
35469             this.select(row, cell);
35470         }
35471     },
35472
35473     /**
35474      * Selects a cell.
35475      * @param {Number} rowIndex
35476      * @param {Number} collIndex
35477      */
35478     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35479         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35480             this.clearSelections();
35481             r = r || this.grid.dataSource.getAt(rowIndex);
35482             this.selection = {
35483                 record : r,
35484                 cell : [rowIndex, colIndex]
35485             };
35486             if(!preventViewNotify){
35487                 var v = this.grid.getView();
35488                 v.onCellSelect(rowIndex, colIndex);
35489                 if(preventFocus !== true){
35490                     v.focusCell(rowIndex, colIndex);
35491                 }
35492             }
35493             this.fireEvent("cellselect", this, rowIndex, colIndex);
35494             this.fireEvent("selectionchange", this, this.selection);
35495         }
35496     },
35497
35498         //private
35499     isSelectable : function(rowIndex, colIndex, cm){
35500         return !cm.isHidden(colIndex);
35501     },
35502
35503     /** @ignore */
35504     handleKeyDown : function(e){
35505         Roo.log('Cell Sel Model handleKeyDown');
35506         if(!e.isNavKeyPress()){
35507             return;
35508         }
35509         var g = this.grid, s = this.selection;
35510         if(!s){
35511             e.stopEvent();
35512             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35513             if(cell){
35514                 this.select(cell[0], cell[1]);
35515             }
35516             return;
35517         }
35518         var sm = this;
35519         var walk = function(row, col, step){
35520             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35521         };
35522         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35523         var newCell;
35524
35525         switch(k){
35526             case e.TAB:
35527                 // handled by onEditorKey
35528                 if (g.isEditor && g.editing) {
35529                     return;
35530                 }
35531                 if(e.shiftKey){
35532                      newCell = walk(r, c-1, -1);
35533                 }else{
35534                      newCell = walk(r, c+1, 1);
35535                 }
35536              break;
35537              case e.DOWN:
35538                  newCell = walk(r+1, c, 1);
35539              break;
35540              case e.UP:
35541                  newCell = walk(r-1, c, -1);
35542              break;
35543              case e.RIGHT:
35544                  newCell = walk(r, c+1, 1);
35545              break;
35546              case e.LEFT:
35547                  newCell = walk(r, c-1, -1);
35548              break;
35549              case e.ENTER:
35550                  if(g.isEditor && !g.editing){
35551                     g.startEditing(r, c);
35552                     e.stopEvent();
35553                     return;
35554                 }
35555              break;
35556         };
35557         if(newCell){
35558             this.select(newCell[0], newCell[1]);
35559             e.stopEvent();
35560         }
35561     },
35562
35563     acceptsNav : function(row, col, cm){
35564         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35565     },
35566
35567     onEditorKey : function(field, e){
35568         
35569         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35570         ///Roo.log('onEditorKey' + k);
35571         
35572         if(k == e.TAB){
35573             if(e.shiftKey){
35574                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35575             }else{
35576                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35577             }
35578             e.stopEvent();
35579         }else if(k == e.ENTER && !e.ctrlKey){
35580             ed.completeEdit();
35581             e.stopEvent();
35582             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35583         }else if(k == e.ESC){
35584             ed.cancelEdit();
35585         }
35586         
35587         
35588         if(newCell){
35589             //Roo.log('next cell after edit');
35590             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35591         }
35592     }
35593 });/*
35594  * Based on:
35595  * Ext JS Library 1.1.1
35596  * Copyright(c) 2006-2007, Ext JS, LLC.
35597  *
35598  * Originally Released Under LGPL - original licence link has changed is not relivant.
35599  *
35600  * Fork - LGPL
35601  * <script type="text/javascript">
35602  */
35603  
35604 /**
35605  * @class Roo.grid.EditorGrid
35606  * @extends Roo.grid.Grid
35607  * Class for creating and editable grid.
35608  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35609  * The container MUST have some type of size defined for the grid to fill. The container will be 
35610  * automatically set to position relative if it isn't already.
35611  * @param {Object} dataSource The data model to bind to
35612  * @param {Object} colModel The column model with info about this grid's columns
35613  */
35614 Roo.grid.EditorGrid = function(container, config){
35615     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35616     this.getGridEl().addClass("xedit-grid");
35617
35618     if(!this.selModel){
35619         this.selModel = new Roo.grid.CellSelectionModel();
35620     }
35621
35622     this.activeEditor = null;
35623
35624         this.addEvents({
35625             /**
35626              * @event beforeedit
35627              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35628              * <ul style="padding:5px;padding-left:16px;">
35629              * <li>grid - This grid</li>
35630              * <li>record - The record being edited</li>
35631              * <li>field - The field name being edited</li>
35632              * <li>value - The value for the field being edited.</li>
35633              * <li>row - The grid row index</li>
35634              * <li>column - The grid column index</li>
35635              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35636              * </ul>
35637              * @param {Object} e An edit event (see above for description)
35638              */
35639             "beforeedit" : true,
35640             /**
35641              * @event afteredit
35642              * Fires after a cell is edited. <br />
35643              * <ul style="padding:5px;padding-left:16px;">
35644              * <li>grid - This grid</li>
35645              * <li>record - The record being edited</li>
35646              * <li>field - The field name being edited</li>
35647              * <li>value - The value being set</li>
35648              * <li>originalValue - The original value for the field, before the edit.</li>
35649              * <li>row - The grid row index</li>
35650              * <li>column - The grid column index</li>
35651              * </ul>
35652              * @param {Object} e An edit event (see above for description)
35653              */
35654             "afteredit" : true,
35655             /**
35656              * @event validateedit
35657              * Fires after a cell is edited, but before the value is set in the record. 
35658          * You can use this to modify the value being set in the field, Return false
35659              * to cancel the change. The edit event object has the following properties <br />
35660              * <ul style="padding:5px;padding-left:16px;">
35661          * <li>editor - This editor</li>
35662              * <li>grid - This grid</li>
35663              * <li>record - The record being edited</li>
35664              * <li>field - The field name being edited</li>
35665              * <li>value - The value being set</li>
35666              * <li>originalValue - The original value for the field, before the edit.</li>
35667              * <li>row - The grid row index</li>
35668              * <li>column - The grid column index</li>
35669              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35670              * </ul>
35671              * @param {Object} e An edit event (see above for description)
35672              */
35673             "validateedit" : true
35674         });
35675     this.on("bodyscroll", this.stopEditing,  this);
35676     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35677 };
35678
35679 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35680     /**
35681      * @cfg {Number} clicksToEdit
35682      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35683      */
35684     clicksToEdit: 2,
35685
35686     // private
35687     isEditor : true,
35688     // private
35689     trackMouseOver: false, // causes very odd FF errors
35690
35691     onCellDblClick : function(g, row, col){
35692         this.startEditing(row, col);
35693     },
35694
35695     onEditComplete : function(ed, value, startValue){
35696         this.editing = false;
35697         this.activeEditor = null;
35698         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35699         var r = ed.record;
35700         var field = this.colModel.getDataIndex(ed.col);
35701         var e = {
35702             grid: this,
35703             record: r,
35704             field: field,
35705             originalValue: startValue,
35706             value: value,
35707             row: ed.row,
35708             column: ed.col,
35709             cancel:false,
35710             editor: ed
35711         };
35712         if(String(value) !== String(startValue)){
35713             
35714             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35715                 r.set(field, e.value);
35716                 // if we are dealing with a combo box..
35717                 // then we also set the 'name' colum to be the displayField
35718                 if (ed.field.displayField && ed.field.name) {
35719                     r.set(ed.field.name, ed.field.el.dom.value);
35720                 }
35721                 
35722                 delete e.cancel; //?? why!!!
35723                 this.fireEvent("afteredit", e);
35724             }
35725         } else {
35726             this.fireEvent("afteredit", e); // always fire it!
35727         }
35728         this.view.focusCell(ed.row, ed.col);
35729     },
35730
35731     /**
35732      * Starts editing the specified for the specified row/column
35733      * @param {Number} rowIndex
35734      * @param {Number} colIndex
35735      */
35736     startEditing : function(row, col){
35737         this.stopEditing();
35738         if(this.colModel.isCellEditable(col, row)){
35739             this.view.ensureVisible(row, col, true);
35740             var r = this.dataSource.getAt(row);
35741             var field = this.colModel.getDataIndex(col);
35742             var e = {
35743                 grid: this,
35744                 record: r,
35745                 field: field,
35746                 value: r.data[field],
35747                 row: row,
35748                 column: col,
35749                 cancel:false
35750             };
35751             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35752                 this.editing = true;
35753                 var ed = this.colModel.getCellEditor(col, row);
35754                 
35755                 if (!ed) {
35756                     return;
35757                 }
35758                 if(!ed.rendered){
35759                     ed.render(ed.parentEl || document.body);
35760                 }
35761                 ed.field.reset();
35762                 (function(){ // complex but required for focus issues in safari, ie and opera
35763                     ed.row = row;
35764                     ed.col = col;
35765                     ed.record = r;
35766                     ed.on("complete", this.onEditComplete, this, {single: true});
35767                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35768                     this.activeEditor = ed;
35769                     var v = r.data[field];
35770                     ed.startEdit(this.view.getCell(row, col), v);
35771                     // combo's with 'displayField and name set
35772                     if (ed.field.displayField && ed.field.name) {
35773                         ed.field.el.dom.value = r.data[ed.field.name];
35774                     }
35775                     
35776                     
35777                 }).defer(50, this);
35778             }
35779         }
35780     },
35781         
35782     /**
35783      * Stops any active editing
35784      */
35785     stopEditing : function(){
35786         if(this.activeEditor){
35787             this.activeEditor.completeEdit();
35788         }
35789         this.activeEditor = null;
35790     }
35791 });/*
35792  * Based on:
35793  * Ext JS Library 1.1.1
35794  * Copyright(c) 2006-2007, Ext JS, LLC.
35795  *
35796  * Originally Released Under LGPL - original licence link has changed is not relivant.
35797  *
35798  * Fork - LGPL
35799  * <script type="text/javascript">
35800  */
35801
35802 // private - not really -- you end up using it !
35803 // This is a support class used internally by the Grid components
35804
35805 /**
35806  * @class Roo.grid.GridEditor
35807  * @extends Roo.Editor
35808  * Class for creating and editable grid elements.
35809  * @param {Object} config any settings (must include field)
35810  */
35811 Roo.grid.GridEditor = function(field, config){
35812     if (!config && field.field) {
35813         config = field;
35814         field = Roo.factory(config.field, Roo.form);
35815     }
35816     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35817     field.monitorTab = false;
35818 };
35819
35820 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35821     
35822     /**
35823      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35824      */
35825     
35826     alignment: "tl-tl",
35827     autoSize: "width",
35828     hideEl : false,
35829     cls: "x-small-editor x-grid-editor",
35830     shim:false,
35831     shadow:"frame"
35832 });/*
35833  * Based on:
35834  * Ext JS Library 1.1.1
35835  * Copyright(c) 2006-2007, Ext JS, LLC.
35836  *
35837  * Originally Released Under LGPL - original licence link has changed is not relivant.
35838  *
35839  * Fork - LGPL
35840  * <script type="text/javascript">
35841  */
35842   
35843
35844   
35845 Roo.grid.PropertyRecord = Roo.data.Record.create([
35846     {name:'name',type:'string'},  'value'
35847 ]);
35848
35849
35850 Roo.grid.PropertyStore = function(grid, source){
35851     this.grid = grid;
35852     this.store = new Roo.data.Store({
35853         recordType : Roo.grid.PropertyRecord
35854     });
35855     this.store.on('update', this.onUpdate,  this);
35856     if(source){
35857         this.setSource(source);
35858     }
35859     Roo.grid.PropertyStore.superclass.constructor.call(this);
35860 };
35861
35862
35863
35864 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35865     setSource : function(o){
35866         this.source = o;
35867         this.store.removeAll();
35868         var data = [];
35869         for(var k in o){
35870             if(this.isEditableValue(o[k])){
35871                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35872             }
35873         }
35874         this.store.loadRecords({records: data}, {}, true);
35875     },
35876
35877     onUpdate : function(ds, record, type){
35878         if(type == Roo.data.Record.EDIT){
35879             var v = record.data['value'];
35880             var oldValue = record.modified['value'];
35881             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35882                 this.source[record.id] = v;
35883                 record.commit();
35884                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35885             }else{
35886                 record.reject();
35887             }
35888         }
35889     },
35890
35891     getProperty : function(row){
35892        return this.store.getAt(row);
35893     },
35894
35895     isEditableValue: function(val){
35896         if(val && val instanceof Date){
35897             return true;
35898         }else if(typeof val == 'object' || typeof val == 'function'){
35899             return false;
35900         }
35901         return true;
35902     },
35903
35904     setValue : function(prop, value){
35905         this.source[prop] = value;
35906         this.store.getById(prop).set('value', value);
35907     },
35908
35909     getSource : function(){
35910         return this.source;
35911     }
35912 });
35913
35914 Roo.grid.PropertyColumnModel = function(grid, store){
35915     this.grid = grid;
35916     var g = Roo.grid;
35917     g.PropertyColumnModel.superclass.constructor.call(this, [
35918         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
35919         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
35920     ]);
35921     this.store = store;
35922     this.bselect = Roo.DomHelper.append(document.body, {
35923         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
35924             {tag: 'option', value: 'true', html: 'true'},
35925             {tag: 'option', value: 'false', html: 'false'}
35926         ]
35927     });
35928     Roo.id(this.bselect);
35929     var f = Roo.form;
35930     this.editors = {
35931         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
35932         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
35933         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
35934         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
35935         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
35936     };
35937     this.renderCellDelegate = this.renderCell.createDelegate(this);
35938     this.renderPropDelegate = this.renderProp.createDelegate(this);
35939 };
35940
35941 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
35942     
35943     
35944     nameText : 'Name',
35945     valueText : 'Value',
35946     
35947     dateFormat : 'm/j/Y',
35948     
35949     
35950     renderDate : function(dateVal){
35951         return dateVal.dateFormat(this.dateFormat);
35952     },
35953
35954     renderBool : function(bVal){
35955         return bVal ? 'true' : 'false';
35956     },
35957
35958     isCellEditable : function(colIndex, rowIndex){
35959         return colIndex == 1;
35960     },
35961
35962     getRenderer : function(col){
35963         return col == 1 ?
35964             this.renderCellDelegate : this.renderPropDelegate;
35965     },
35966
35967     renderProp : function(v){
35968         return this.getPropertyName(v);
35969     },
35970
35971     renderCell : function(val){
35972         var rv = val;
35973         if(val instanceof Date){
35974             rv = this.renderDate(val);
35975         }else if(typeof val == 'boolean'){
35976             rv = this.renderBool(val);
35977         }
35978         return Roo.util.Format.htmlEncode(rv);
35979     },
35980
35981     getPropertyName : function(name){
35982         var pn = this.grid.propertyNames;
35983         return pn && pn[name] ? pn[name] : name;
35984     },
35985
35986     getCellEditor : function(colIndex, rowIndex){
35987         var p = this.store.getProperty(rowIndex);
35988         var n = p.data['name'], val = p.data['value'];
35989         
35990         if(typeof(this.grid.customEditors[n]) == 'string'){
35991             return this.editors[this.grid.customEditors[n]];
35992         }
35993         if(typeof(this.grid.customEditors[n]) != 'undefined'){
35994             return this.grid.customEditors[n];
35995         }
35996         if(val instanceof Date){
35997             return this.editors['date'];
35998         }else if(typeof val == 'number'){
35999             return this.editors['number'];
36000         }else if(typeof val == 'boolean'){
36001             return this.editors['boolean'];
36002         }else{
36003             return this.editors['string'];
36004         }
36005     }
36006 });
36007
36008 /**
36009  * @class Roo.grid.PropertyGrid
36010  * @extends Roo.grid.EditorGrid
36011  * This class represents the  interface of a component based property grid control.
36012  * <br><br>Usage:<pre><code>
36013  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36014       
36015  });
36016  // set any options
36017  grid.render();
36018  * </code></pre>
36019   
36020  * @constructor
36021  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36022  * The container MUST have some type of size defined for the grid to fill. The container will be
36023  * automatically set to position relative if it isn't already.
36024  * @param {Object} config A config object that sets properties on this grid.
36025  */
36026 Roo.grid.PropertyGrid = function(container, config){
36027     config = config || {};
36028     var store = new Roo.grid.PropertyStore(this);
36029     this.store = store;
36030     var cm = new Roo.grid.PropertyColumnModel(this, store);
36031     store.store.sort('name', 'ASC');
36032     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36033         ds: store.store,
36034         cm: cm,
36035         enableColLock:false,
36036         enableColumnMove:false,
36037         stripeRows:false,
36038         trackMouseOver: false,
36039         clicksToEdit:1
36040     }, config));
36041     this.getGridEl().addClass('x-props-grid');
36042     this.lastEditRow = null;
36043     this.on('columnresize', this.onColumnResize, this);
36044     this.addEvents({
36045          /**
36046              * @event beforepropertychange
36047              * Fires before a property changes (return false to stop?)
36048              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36049              * @param {String} id Record Id
36050              * @param {String} newval New Value
36051          * @param {String} oldval Old Value
36052              */
36053         "beforepropertychange": true,
36054         /**
36055              * @event propertychange
36056              * Fires after a property changes
36057              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36058              * @param {String} id Record Id
36059              * @param {String} newval New Value
36060          * @param {String} oldval Old Value
36061              */
36062         "propertychange": true
36063     });
36064     this.customEditors = this.customEditors || {};
36065 };
36066 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36067     
36068      /**
36069      * @cfg {Object} customEditors map of colnames=> custom editors.
36070      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36071      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36072      * false disables editing of the field.
36073          */
36074     
36075       /**
36076      * @cfg {Object} propertyNames map of property Names to their displayed value
36077          */
36078     
36079     render : function(){
36080         Roo.grid.PropertyGrid.superclass.render.call(this);
36081         this.autoSize.defer(100, this);
36082     },
36083
36084     autoSize : function(){
36085         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36086         if(this.view){
36087             this.view.fitColumns();
36088         }
36089     },
36090
36091     onColumnResize : function(){
36092         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36093         this.autoSize();
36094     },
36095     /**
36096      * Sets the data for the Grid
36097      * accepts a Key => Value object of all the elements avaiable.
36098      * @param {Object} data  to appear in grid.
36099      */
36100     setSource : function(source){
36101         this.store.setSource(source);
36102         //this.autoSize();
36103     },
36104     /**
36105      * Gets all the data from the grid.
36106      * @return {Object} data  data stored in grid
36107      */
36108     getSource : function(){
36109         return this.store.getSource();
36110     }
36111 });/*
36112  * Based on:
36113  * Ext JS Library 1.1.1
36114  * Copyright(c) 2006-2007, Ext JS, LLC.
36115  *
36116  * Originally Released Under LGPL - original licence link has changed is not relivant.
36117  *
36118  * Fork - LGPL
36119  * <script type="text/javascript">
36120  */
36121  
36122 /**
36123  * @class Roo.LoadMask
36124  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36125  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36126  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36127  * element's UpdateManager load indicator and will be destroyed after the initial load.
36128  * @constructor
36129  * Create a new LoadMask
36130  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36131  * @param {Object} config The config object
36132  */
36133 Roo.LoadMask = function(el, config){
36134     this.el = Roo.get(el);
36135     Roo.apply(this, config);
36136     if(this.store){
36137         this.store.on('beforeload', this.onBeforeLoad, this);
36138         this.store.on('load', this.onLoad, this);
36139         this.store.on('loadexception', this.onLoad, this);
36140         this.removeMask = false;
36141     }else{
36142         var um = this.el.getUpdateManager();
36143         um.showLoadIndicator = false; // disable the default indicator
36144         um.on('beforeupdate', this.onBeforeLoad, this);
36145         um.on('update', this.onLoad, this);
36146         um.on('failure', this.onLoad, this);
36147         this.removeMask = true;
36148     }
36149 };
36150
36151 Roo.LoadMask.prototype = {
36152     /**
36153      * @cfg {Boolean} removeMask
36154      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36155      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36156      */
36157     /**
36158      * @cfg {String} msg
36159      * The text to display in a centered loading message box (defaults to 'Loading...')
36160      */
36161     msg : 'Loading...',
36162     /**
36163      * @cfg {String} msgCls
36164      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36165      */
36166     msgCls : 'x-mask-loading',
36167
36168     /**
36169      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36170      * @type Boolean
36171      */
36172     disabled: false,
36173
36174     /**
36175      * Disables the mask to prevent it from being displayed
36176      */
36177     disable : function(){
36178        this.disabled = true;
36179     },
36180
36181     /**
36182      * Enables the mask so that it can be displayed
36183      */
36184     enable : function(){
36185         this.disabled = false;
36186     },
36187
36188     // private
36189     onLoad : function(){
36190         this.el.unmask(this.removeMask);
36191     },
36192
36193     // private
36194     onBeforeLoad : function(){
36195         if(!this.disabled){
36196             this.el.mask(this.msg, this.msgCls);
36197         }
36198     },
36199
36200     // private
36201     destroy : function(){
36202         if(this.store){
36203             this.store.un('beforeload', this.onBeforeLoad, this);
36204             this.store.un('load', this.onLoad, this);
36205             this.store.un('loadexception', this.onLoad, this);
36206         }else{
36207             var um = this.el.getUpdateManager();
36208             um.un('beforeupdate', this.onBeforeLoad, this);
36209             um.un('update', this.onLoad, this);
36210             um.un('failure', this.onLoad, this);
36211         }
36212     }
36213 };/*
36214  * Based on:
36215  * Ext JS Library 1.1.1
36216  * Copyright(c) 2006-2007, Ext JS, LLC.
36217  *
36218  * Originally Released Under LGPL - original licence link has changed is not relivant.
36219  *
36220  * Fork - LGPL
36221  * <script type="text/javascript">
36222  */
36223 Roo.XTemplate = function(){
36224     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36225     var s = this.html;
36226
36227     s = ['<tpl>', s, '</tpl>'].join('');
36228
36229     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36230
36231     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36232     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36233     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36234     var m, id = 0;
36235     var tpls = [];
36236
36237     while(m = s.match(re)){
36238        var m2 = m[0].match(nameRe);
36239        var m3 = m[0].match(ifRe);
36240        var m4 = m[0].match(execRe);
36241        var exp = null, fn = null, exec = null;
36242        var name = m2 && m2[1] ? m2[1] : '';
36243        if(m3){
36244            exp = m3 && m3[1] ? m3[1] : null;
36245            if(exp){
36246                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36247            }
36248        }
36249        if(m4){
36250            exp = m4 && m4[1] ? m4[1] : null;
36251            if(exp){
36252                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36253            }
36254        }
36255        if(name){
36256            switch(name){
36257                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36258                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36259                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36260            }
36261        }
36262        tpls.push({
36263             id: id,
36264             target: name,
36265             exec: exec,
36266             test: fn,
36267             body: m[1]||''
36268         });
36269        s = s.replace(m[0], '{xtpl'+ id + '}');
36270        ++id;
36271     }
36272     for(var i = tpls.length-1; i >= 0; --i){
36273         this.compileTpl(tpls[i]);
36274     }
36275     this.master = tpls[tpls.length-1];
36276     this.tpls = tpls;
36277 };
36278 Roo.extend(Roo.XTemplate, Roo.Template, {
36279
36280     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36281
36282     applySubTemplate : function(id, values, parent){
36283         var t = this.tpls[id];
36284         if(t.test && !t.test.call(this, values, parent)){
36285             return '';
36286         }
36287         if(t.exec && t.exec.call(this, values, parent)){
36288             return '';
36289         }
36290         var vs = t.target ? t.target.call(this, values, parent) : values;
36291         parent = t.target ? values : parent;
36292         if(t.target && vs instanceof Array){
36293             var buf = [];
36294             for(var i = 0, len = vs.length; i < len; i++){
36295                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36296             }
36297             return buf.join('');
36298         }
36299         return t.compiled.call(this, vs, parent);
36300     },
36301
36302     compileTpl : function(tpl){
36303         var fm = Roo.util.Format;
36304         var useF = this.disableFormats !== true;
36305         var sep = Roo.isGecko ? "+" : ",";
36306         var fn = function(m, name, format, args){
36307             if(name.substr(0, 4) == 'xtpl'){
36308                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36309             }
36310             var v;
36311             if(name.indexOf('.') != -1){
36312                 v = name;
36313             }else{
36314                 v = "values['" + name + "']";
36315             }
36316             if(format && useF){
36317                 args = args ? ',' + args : "";
36318                 if(format.substr(0, 5) != "this."){
36319                     format = "fm." + format + '(';
36320                 }else{
36321                     format = 'this.call("'+ format.substr(5) + '", ';
36322                     args = ", values";
36323                 }
36324             }else{
36325                 args= ''; format = "("+v+" === undefined ? '' : ";
36326             }
36327             return "'"+ sep + format + v + args + ")"+sep+"'";
36328         };
36329         var body;
36330         // branched to use + in gecko and [].join() in others
36331         if(Roo.isGecko){
36332             body = "tpl.compiled = function(values, parent){ return '" +
36333                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36334                     "';};";
36335         }else{
36336             body = ["tpl.compiled = function(values, parent){ return ['"];
36337             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36338             body.push("'].join('');};");
36339             body = body.join('');
36340         }
36341         /** eval:var:zzzzzzz */
36342         eval(body);
36343         return this;
36344     },
36345
36346     applyTemplate : function(values){
36347         return this.master.compiled.call(this, values, {});
36348         var s = this.subs;
36349     },
36350
36351     apply : function(){
36352         return this.applyTemplate.apply(this, arguments);
36353     },
36354
36355     compile : function(){return this;}
36356 });
36357
36358 Roo.XTemplate.from = function(el){
36359     el = Roo.getDom(el);
36360     return new Roo.XTemplate(el.value || el.innerHTML);
36361 };/*
36362  * Original code for Roojs - LGPL
36363  * <script type="text/javascript">
36364  */
36365  
36366 /**
36367  * @class Roo.XComponent
36368  * A delayed Element creator...
36369  * 
36370  * Mypart.xyx = new Roo.XComponent({
36371
36372     parent : 'Mypart.xyz', // empty == document.element.!!
36373     order : '001',
36374     name : 'xxxx'
36375     region : 'xxxx'
36376     disabled : function() {} 
36377      
36378     tree : function() { // return an tree of xtype declared components
36379         var MODULE = this;
36380         return 
36381         {
36382             xtype : 'NestedLayoutPanel',
36383             // technicall
36384         }
36385      ]
36386  *})
36387  * @extends Roo.util.Observable
36388  * @constructor
36389  * @param cfg {Object} configuration of component
36390  * 
36391  */
36392 Roo.XComponent = function(cfg) {
36393     Roo.apply(this, cfg);
36394     this.addEvents({ 
36395         /**
36396              * @event built
36397              * Fires when this the componnt is built
36398              * @param {Roo.XComponent} c the component
36399              */
36400         'built' : true,
36401         /**
36402              * @event buildcomplete
36403              * Fires on the top level element when all elements have been built
36404              * @param {Roo.XComponent} c the top level component.
36405          */
36406         'buildcomplete' : true
36407         
36408     });
36409     
36410     Roo.XComponent.register(this);
36411     this.modules = false;
36412     this.el = false; // where the layout goes..
36413     
36414     
36415 }
36416 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36417     /**
36418      * @property el
36419      * The created element (with Roo.factory())
36420      * @type {Roo.Layout}
36421      */
36422     el  : false,
36423     
36424     /**
36425      * @property el
36426      * for BC  - use el in new code
36427      * @type {Roo.Layout}
36428      */
36429     panel : false,
36430     
36431     /**
36432      * @property layout
36433      * for BC  - use el in new code
36434      * @type {Roo.Layout}
36435      */
36436     layout : false,
36437     
36438      /**
36439      * @cfg {Function|boolean} disabled
36440      * If this module is disabled by some rule, return true from the funtion
36441      */
36442     disabled : false,
36443     
36444     /**
36445      * @cfg {String} parent 
36446      * Name of parent element which it get xtype added to..
36447      */
36448     parent: false,
36449     
36450     /**
36451      * @cfg {String} order
36452      * Used to set the order in which elements are created (usefull for multiple tabs)
36453      */
36454     
36455     order : false,
36456     /**
36457      * @cfg {String} name
36458      * String to display while loading.
36459      */
36460     name : false,
36461     /**
36462      * @cfg {Array} items
36463      * A single item array - the first element is the root of the tree..
36464      * It's done this way to stay compatible with the Xtype system...
36465      */
36466     items : false
36467      
36468      
36469     
36470 });
36471
36472 Roo.apply(Roo.XComponent, {
36473     
36474     /**
36475      * @property  buildCompleted
36476      * True when the builder has completed building the interface.
36477      * @type Boolean
36478      */
36479     buildCompleted : false,
36480      
36481     /**
36482      * @property  topModule
36483      * the upper most module - uses document.element as it's constructor.
36484      * @type Object
36485      */
36486      
36487     topModule  : false,
36488       
36489     /**
36490      * @property  modules
36491      * array of modules to be created by registration system.
36492      * @type Roo.XComponent
36493      */
36494     
36495     modules : [],
36496       
36497     
36498     /**
36499      * Register components to be built later.
36500      *
36501      * This solves the following issues
36502      * - Building is not done on page load, but after an authentication process has occured.
36503      * - Interface elements are registered on page load
36504      * - Parent Interface elements may not be loaded before child, so this handles that..
36505      * 
36506      *
36507      * example:
36508      * 
36509      * MyApp.register({
36510           order : '000001',
36511           module : 'Pman.Tab.projectMgr',
36512           region : 'center',
36513           parent : 'Pman.layout',
36514           disabled : false,  // or use a function..
36515         })
36516      
36517      * * @param {Object} details about module
36518      */
36519     register : function(obj) {
36520         this.modules.push(obj);
36521          
36522     },
36523     /**
36524      * convert a string to an object..
36525      * 
36526      */
36527     
36528     toObject : function(str)
36529     {
36530         if (!str || typeof(str) == 'object') {
36531             return str;
36532         }
36533         var ar = str.split('.');
36534         var rt, o;
36535         rt = ar.shift();
36536             /** eval:var:o */
36537         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36538         if (o === false) {
36539             throw "Module not found : " + str;
36540         }
36541         Roo.each(ar, function(e) {
36542             if (typeof(o[e]) == 'undefined') {
36543                 throw "Module not found : " + str;
36544             }
36545             o = o[e];
36546         });
36547         return o;
36548         
36549     },
36550     
36551     
36552     /**
36553      * move modules into their correct place in the tree..
36554      * 
36555      */
36556     preBuild : function ()
36557     {
36558         
36559         Roo.each(this.modules , function (obj)
36560         {
36561             obj.parent = this.toObject(obj.parent);
36562             
36563             if (!obj.parent) {
36564                 this.topModule = obj;
36565                 return;
36566             }
36567             
36568             if (!obj.parent.modules) {
36569                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36570                     function(o) { return o.order + '' }
36571                 );
36572             }
36573             
36574             obj.parent.modules.add(obj);
36575         }, this);
36576     },
36577     
36578      /**
36579      * make a list of modules to build.
36580      * @return {Array} list of modules. 
36581      */ 
36582     
36583     buildOrder : function()
36584     {
36585         var _this = this;
36586         var cmp = function(a,b) {   
36587             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36588         };
36589         
36590         if (!this.topModule || !this.topModule.modules) {
36591             throw "No top level modules to build";
36592         }
36593        
36594         // make a flat list in order of modules to build.
36595         var mods = [ this.topModule ];
36596         
36597         
36598         // add modules to their parents..
36599         var addMod = function(m) {
36600            // Roo.debug && Roo.log(m.modKey);
36601             
36602             mods.push(m);
36603             if (m.modules) {
36604                 m.modules.keySort('ASC',  cmp );
36605                 m.modules.each(addMod);
36606             }
36607             // not sure if this is used any more..
36608             if (m.finalize) {
36609                 m.finalize.name = m.name + " (clean up) ";
36610                 mods.push(m.finalize);
36611             }
36612             
36613         }
36614         this.topModule.modules.keySort('ASC',  cmp );
36615         this.topModule.modules.each(addMod);
36616         return mods;
36617     },
36618     
36619      /**
36620      * Build the registered modules.
36621      * @param {Object} parent element.
36622      * @param {Function} optional method to call after module has been added.
36623      * 
36624      */ 
36625    
36626     build : function() 
36627     {
36628         
36629         this.preBuild();
36630         var mods = this.buildOrder();
36631       
36632         //this.allmods = mods;
36633         //Roo.debug && Roo.log(mods);
36634         //return;
36635         if (!mods.length) { // should not happen
36636             throw "NO modules!!!";
36637         }
36638         
36639         
36640         
36641         // flash it up as modal - so we store the mask!?
36642         Roo.MessageBox.show({ title: 'loading' });
36643         Roo.MessageBox.show({
36644            title: "Please wait...",
36645            msg: "Building Interface...",
36646            width:450,
36647            progress:true,
36648            closable:false,
36649            modal: false
36650           
36651         });
36652         var total = mods.length;
36653         
36654         var _this = this;
36655         var progressRun = function() {
36656             if (!mods.length) {
36657                 Roo.debug && Roo.log('hide?');
36658                 Roo.MessageBox.hide();
36659                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36660                 return;    
36661             }
36662             
36663             var m = mods.shift();
36664             Roo.debug && Roo.log(m);
36665             if (typeof(m) == 'function') { // not sure if this is supported any more..
36666                 m.call(this);
36667                 return progressRun.defer(10, _this);
36668             } 
36669             
36670             Roo.MessageBox.updateProgress(
36671                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36672                     " of " + total + 
36673                     (m.name ? (' - ' + m.name) : '')
36674                     );
36675             
36676          
36677             
36678             var disabled = (typeof(m.disabled) == 'function') ?
36679                 m.disabled.call(m.module.disabled) : m.disabled;    
36680             
36681             
36682             if (disabled) {
36683                 return progressRun(); // we do not update the display!
36684             }
36685             
36686             if (!m.parent) {
36687                 // it's a top level one..
36688                 var layoutbase = new Ext.BorderLayout(document.body, {
36689                
36690                     center: {
36691                          titlebar: false,
36692                          autoScroll:false,
36693                          closeOnTab: true,
36694                          tabPosition: 'top',
36695                          //resizeTabs: true,
36696                          alwaysShowTabs: true,
36697                          minTabWidth: 140
36698                     }
36699                 });
36700                 var tree = m.tree();
36701                 tree.region = 'center';
36702                 m.el = layoutbase.addxtype(tree);
36703                 m.panel = m.el;
36704                 m.layout = m.panel.layout;    
36705                 return progressRun.defer(10, _this);
36706             }
36707             
36708             var tree = m.tree();
36709             tree.region = tree.region || m.region;
36710             m.el = m.parent.el.addxtype(tree);
36711             m.fireEvent('built', m);
36712             m.panel = m.el;
36713             m.layout = m.panel.layout;    
36714             progressRun.defer(10, _this); 
36715             
36716         }
36717         progressRun.defer(1, _this);
36718      
36719         
36720         
36721     }
36722      
36723    
36724     
36725     
36726 });
36727  //<script type="text/javascript">
36728
36729
36730 /**
36731  * @class Roo.Login
36732  * @extends Roo.LayoutDialog
36733  * A generic Login Dialog..... - only one needed in theory!?!?
36734  *
36735  * Fires XComponent builder on success...
36736  * 
36737  * Sends 
36738  *    username,password, lang = for login actions.
36739  *    check = 1 for periodic checking that sesion is valid.
36740  *    passwordRequest = email request password
36741  *    logout = 1 = to logout
36742  * 
36743  * Affects: (this id="????" elements)
36744  *   loading  (removed) (used to indicate application is loading)
36745  *   loading-mask (hides) (used to hide application when it's building loading)
36746  *   
36747  * 
36748  * Usage: 
36749  *    
36750  * 
36751  * Myapp.login = Roo.Login({
36752      url: xxxx,
36753    
36754      realm : 'Myapp', 
36755      
36756      
36757      method : 'POST',
36758      
36759      
36760      * 
36761  })
36762  * 
36763  * 
36764  * 
36765  **/
36766  
36767 Roo.Login = function(cfg)
36768 {
36769     this.addEvents({
36770         'refreshed' : true
36771     });
36772     
36773     Roo.apply(this,cfg);
36774     
36775     Roo.onReady(function() {
36776         this.onLoad();
36777     }, this);
36778     // call parent..
36779     
36780    
36781     Roo.Login.superclass.constructor.call(this, this);
36782     //this.addxtype(this.items[0]);
36783     
36784     
36785 }
36786
36787
36788 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36789     
36790     /**
36791      * @cfg {String} method
36792      * Method used to query for login details.
36793      */
36794     
36795     method : 'POST',
36796     /**
36797      * @cfg {String} url
36798      * URL to query login data. - eg. baseURL + '/Login.php'
36799      */
36800     url : '',
36801     
36802     /**
36803      * @property user
36804      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36805      * @type {Object} 
36806      */
36807     user : false,
36808     /**
36809      * @property checkFails
36810      * Number of times we have attempted to get authentication check, and failed.
36811      * @type {Number} 
36812      */
36813     checkFails : 0,
36814       /**
36815      * @property intervalID
36816      * The window interval that does the constant login checking.
36817      * @type {Number} 
36818      */
36819     intervalID : 0,
36820     
36821     
36822     onLoad : function() // called on page load...
36823     {
36824         // load 
36825          
36826         if (Roo.get('loading')) { // clear any loading indicator..
36827             Roo.get('loading').remove();
36828         }
36829         
36830         //this.switchLang('en'); // set the language to english..
36831        
36832         this.check({
36833             success:  function(response, opts)  {  // check successfull...
36834             
36835                 var res = this.processResponse(response);
36836                 this.checkFails =0;
36837                 if (!res.success) { // error!
36838                     this.checkFails = 5;
36839                     //console.log('call failure');
36840                     return this.failure(response,opts);
36841                 }
36842                 
36843                 if (!res.data.id) { // id=0 == login failure.
36844                     return this.show();
36845                 }
36846                 
36847                               
36848                         //console.log(success);
36849                 this.fillAuth(res.data);   
36850                 this.checkFails =0;
36851                 Roo.XComponent.build();
36852             },
36853             failure : this.show
36854         });
36855         
36856     }, 
36857     
36858     
36859     check: function(cfg) // called every so often to refresh cookie etc..
36860     {
36861         if (cfg.again) { // could be undefined..
36862             this.checkFails++;
36863         } else {
36864             this.checkFails = 0;
36865         }
36866         var _this = this;
36867         if (this.sending) {
36868             if ( this.checkFails > 4) {
36869                 Roo.MessageBox.alert("Error",  
36870                     "Error getting authentication status. - try reloading, or wait a while", function() {
36871                         _this.sending = false;
36872                     }); 
36873                 return;
36874             }
36875             cfg.again = true;
36876             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36877             return;
36878         }
36879         this.sending = true;
36880         
36881         Roo.Ajax.request({  
36882             url: this.url,
36883             params: {
36884                 getAuthUser: true
36885             },  
36886             method: this.method,
36887             success:  cfg.success || this.success,
36888             failure : cfg.failure || this.failure,
36889             scope : this,
36890             callCfg : cfg
36891               
36892         });  
36893     }, 
36894     
36895     
36896     logout: function()
36897     {
36898         window.onbeforeunload = function() { }; // false does not work for IE..
36899         this.user = false;
36900         var _this = this;
36901         
36902         Roo.Ajax.request({  
36903             url: this.url,
36904             params: {
36905                 logout: 1
36906             },  
36907             method: 'GET',
36908             failure : function() {
36909                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36910                     document.location = document.location.toString() + '?ts=' + Math.random();
36911                 });
36912                 
36913             },
36914             success : function() {
36915                 _this.user = false;
36916                 this.checkFails =0;
36917                 // fixme..
36918                 document.location = document.location.toString() + '?ts=' + Math.random();
36919             }
36920               
36921               
36922         }); 
36923     },
36924     
36925     processResponse : function (response)
36926     {
36927         var res = '';
36928         try {
36929             res = Roo.decode(response.responseText);
36930             // oops...
36931             if (typeof(res) != 'object') {
36932                 res = { success : false, errorMsg : res, errors : true };
36933             }
36934             if (typeof(res.success) == 'undefined') {
36935                 res.success = false;
36936             }
36937             
36938         } catch(e) {
36939             res = { success : false,  errorMsg : response.responseText, errors : true };
36940         }
36941         return res;
36942     },
36943     
36944     success : function(response, opts)  // check successfull...
36945     {  
36946         this.sending = false;
36947         var res = this.processResponse(response);
36948         if (!res.success) {
36949             return this.failure(response, opts);
36950         }
36951         if (!res.data || !res.data.id) {
36952             return this.failure(response,opts);
36953         }
36954         //console.log(res);
36955         this.fillAuth(res.data);
36956         
36957         this.checkFails =0;
36958         
36959     },
36960     
36961     
36962     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
36963     {
36964         this.authUser = -1;
36965         this.sending = false;
36966         var res = this.processResponse(response);
36967         //console.log(res);
36968         if ( this.checkFails > 2) {
36969         
36970             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
36971                 "Error getting authentication status. - try reloading"); 
36972             return;
36973         }
36974         opts.callCfg.again = true;
36975         this.check.defer(1000, this, [ opts.callCfg ]);
36976         return;  
36977     },
36978     
36979     
36980     
36981     fillAuth: function(au) {
36982         this.startAuthCheck();
36983         this.authUserId = au.id;
36984         this.authUser = au;
36985         this.lastChecked = new Date();
36986         this.fireEvent('refreshed', au);
36987         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
36988         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
36989         au.lang = au.lang || 'en';
36990         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
36991         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
36992         this.switchLang(au.lang );
36993         
36994      
36995         // open system... - -on setyp..
36996         if (this.authUserId  < 0) {
36997             Roo.MessageBox.alert("Warning", 
36998                 "This is an open system - please set up a admin user with a password.");  
36999         }
37000          
37001         //Pman.onload(); // which should do nothing if it's a re-auth result...
37002         
37003              
37004     },
37005     
37006     startAuthCheck : function() // starter for timeout checking..
37007     {
37008         if (this.intervalID) { // timer already in place...
37009             return false;
37010         }
37011         var _this = this;
37012         this.intervalID =  window.setInterval(function() {
37013               _this.check(false);
37014             }, 120000); // every 120 secs = 2mins..
37015         
37016         
37017     },
37018          
37019     
37020     switchLang : function (lang) 
37021     {
37022         _T = typeof(_T) == 'undefined' ? false : _T;
37023           if (!_T || !lang.length) {
37024             return;
37025         }
37026         
37027         if (!_T && lang != 'en') {
37028             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37029             return;
37030         }
37031         
37032         if (typeof(_T.en) == 'undefined') {
37033             _T.en = {};
37034             Roo.apply(_T.en, _T);
37035         }
37036         
37037         if (typeof(_T[lang]) == 'undefined') {
37038             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37039             return;
37040         }
37041         
37042         
37043         Roo.apply(_T, _T[lang]);
37044         // just need to set the text values for everything...
37045         var _this = this;
37046         /* this will not work ...
37047         if (this.form) { 
37048             
37049                
37050             function formLabel(name, val) {
37051                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37052             }
37053             
37054             formLabel('password', "Password"+':');
37055             formLabel('username', "Email Address"+':');
37056             formLabel('lang', "Language"+':');
37057             this.dialog.setTitle("Login");
37058             this.dialog.buttons[0].setText("Forgot Password");
37059             this.dialog.buttons[1].setText("Login");
37060         }
37061         */
37062         
37063         
37064     },
37065     
37066     
37067     title: "Login",
37068     modal: true,
37069     width:  350,
37070     //height: 230,
37071     height: 180,
37072     shadow: true,
37073     minWidth:200,
37074     minHeight:180,
37075     //proxyDrag: true,
37076     closable: false,
37077     draggable: false,
37078     collapsible: false,
37079     resizable: false,
37080     center: {  // needed??
37081         autoScroll:false,
37082         titlebar: false,
37083        // tabPosition: 'top',
37084         hideTabs: true,
37085         closeOnTab: true,
37086         alwaysShowTabs: false
37087     } ,
37088     listeners : {
37089         
37090         show  : function(dlg)
37091         {
37092             //console.log(this);
37093             this.form = this.layout.getRegion('center').activePanel.form;
37094             this.form.dialog = dlg;
37095             this.buttons[0].form = this.form;
37096             this.buttons[0].dialog = dlg;
37097             this.buttons[1].form = this.form;
37098             this.buttons[1].dialog = dlg;
37099            
37100            //this.resizeToLogo.defer(1000,this);
37101             // this is all related to resizing for logos..
37102             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37103            //// if (!sz) {
37104              //   this.resizeToLogo.defer(1000,this);
37105              //   return;
37106            // }
37107             //var w = Ext.lib.Dom.getViewWidth() - 100;
37108             //var h = Ext.lib.Dom.getViewHeight() - 100;
37109             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37110             //this.center();
37111             if (this.disabled) {
37112                 this.hide();
37113                 return;
37114             }
37115             
37116             if (this.user.id < 0) { // used for inital setup situations.
37117                 return;
37118             }
37119             
37120             if (this.intervalID) {
37121                 // remove the timer
37122                 window.clearInterval(this.intervalID);
37123                 this.intervalID = false;
37124             }
37125             
37126             
37127             if (Roo.get('loading')) {
37128                 Roo.get('loading').remove();
37129             }
37130             if (Roo.get('loading-mask')) {
37131                 Roo.get('loading-mask').hide();
37132             }
37133             
37134             //incomming._node = tnode;
37135             this.form.reset();
37136             //this.dialog.modal = !modal;
37137             //this.dialog.show();
37138             this.el.unmask(); 
37139             
37140             
37141             this.form.setValues({
37142                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37143                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37144             });
37145             
37146             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37147             if (this.form.findField('username').getValue().length > 0 ){
37148                 this.form.findField('password').focus();
37149             } else {
37150                this.form.findField('username').focus();
37151             }
37152     
37153         }
37154     },
37155     items : [
37156          {
37157        
37158             xtype : 'ContentPanel',
37159             xns : Roo,
37160             region: 'center',
37161             fitToFrame : true,
37162             
37163             items : [
37164     
37165                 {
37166                
37167                     xtype : 'Form',
37168                     xns : Roo.form,
37169                     labelWidth: 100,
37170                     style : 'margin: 10px;',
37171                     
37172                     listeners : {
37173                         actionfailed : function(f, act) {
37174                             // form can return { errors: .... }
37175                                 
37176                             //act.result.errors // invalid form element list...
37177                             //act.result.errorMsg// invalid form element list...
37178                             
37179                             this.dialog.el.unmask();
37180                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37181                                         "Login failed - communication error - try again.");
37182                                       
37183                         },
37184                         actioncomplete: function(re, act) {
37185                              
37186                             Roo.state.Manager.set(
37187                                 this.dialog.realm + '.username',  
37188                                     this.findField('username').getValue()
37189                             );
37190                             Roo.state.Manager.set(
37191                                 this.dialog.realm + '.lang',  
37192                                 this.findField('lang').getValue() 
37193                             );
37194                             
37195                             this.dialog.fillAuth(act.result.data);
37196                               
37197                             this.dialog.hide();
37198                             
37199                             if (Roo.get('loading-mask')) {
37200                                 Roo.get('loading-mask').show();
37201                             }
37202                             Roo.XComponent.build();
37203                             
37204                              
37205                             
37206                         }
37207                     },
37208                     items : [
37209                         {
37210                             xtype : 'TextField',
37211                             xns : Roo.form,
37212                             fieldLabel: "Email Address",
37213                             name: 'username',
37214                             width:200,
37215                             autoCreate : {tag: "input", type: "text", size: "20"}
37216                         },
37217                         {
37218                             xtype : 'TextField',
37219                             xns : Roo.form,
37220                             fieldLabel: "Password",
37221                             inputType: 'password',
37222                             name: 'password',
37223                             width:200,
37224                             autoCreate : {tag: "input", type: "text", size: "20"},
37225                             listeners : {
37226                                 specialkey : function(e,ev) {
37227                                     if (ev.keyCode == 13) {
37228                                         this.form.dialog.el.mask("Logging in");
37229                                         this.form.doAction('submit', {
37230                                             url: this.form.dialog.url,
37231                                             method: this.form.dialog.method
37232                                         });
37233                                     }
37234                                 }
37235                             }  
37236                         },
37237                         {
37238                             xtype : 'ComboBox',
37239                             xns : Roo.form,
37240                             fieldLabel: "Language",
37241                             name : 'langdisp',
37242                             store: {
37243                                 xtype : 'SimpleStore',
37244                                 fields: ['lang', 'ldisp'],
37245                                 data : [
37246                                     [ 'en', 'English' ],
37247                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37248                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37249                                 ]
37250                             },
37251                             
37252                             valueField : 'lang',
37253                             hiddenName:  'lang',
37254                             width: 200,
37255                             displayField:'ldisp',
37256                             typeAhead: false,
37257                             editable: false,
37258                             mode: 'local',
37259                             triggerAction: 'all',
37260                             emptyText:'Select a Language...',
37261                             selectOnFocus:true,
37262                             listeners : {
37263                                 select :  function(cb, rec, ix) {
37264                                     this.form.switchLang(rec.data.lang);
37265                                 }
37266                             }
37267                         
37268                         }
37269                     ]
37270                 }
37271                   
37272                 
37273             ]
37274         }
37275     ],
37276     buttons : [
37277         {
37278             xtype : 'Button',
37279             xns : 'Roo',
37280             text : "Forgot Password",
37281             listeners : {
37282                 click : function() {
37283                     //console.log(this);
37284                     var n = this.form.findField('username').getValue();
37285                     if (!n.length) {
37286                         Roo.MessageBox.alert("Error", "Fill in your email address");
37287                         return;
37288                     }
37289                     Roo.Ajax.request({
37290                         url: this.dialog.url,
37291                         params: {
37292                             passwordRequest: n
37293                         },
37294                         method: this.dialog.method,
37295                         success:  function(response, opts)  {  // check successfull...
37296                         
37297                             var res = this.dialog.processResponse(response);
37298                             if (!res.success) { // error!
37299                                Roo.MessageBox.alert("Error" ,
37300                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37301                                return;
37302                             }
37303                             Roo.MessageBox.alert("Notice" ,
37304                                 "Please check you email for the Password Reset message");
37305                         },
37306                         failure : function() {
37307                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37308                         }
37309                         
37310                     });
37311                 }
37312             }
37313         },
37314         {
37315             xtype : 'Button',
37316             xns : 'Roo',
37317             text : "Login",
37318             listeners : {
37319                 
37320                 click : function () {
37321                         
37322                     this.dialog.el.mask("Logging in");
37323                     this.form.doAction('submit', {
37324                             url: this.dialog.url,
37325                             method: this.dialog.method
37326                     });
37327                 }
37328             }
37329         }
37330     ]
37331   
37332   
37333 })
37334  
37335
37336
37337