7dcba74e334e54e16dd234fb4bfb049247e12ae6
[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(this.sortInfo && this.remoteSort){
4931                 var pn = this.paramNames;
4932                 p[pn["sort"]] = this.sortInfo.field;
4933                 p[pn["dir"]] = this.sortInfo.direction;
4934             }
4935             this.proxy.load(p, this.reader, this.loadRecords, this, options);
4936         }
4937     },
4938
4939     /**
4940      * Reloads the Record cache from the configured Proxy using the configured Reader and
4941      * the options from the last load operation performed.
4942      * @param {Object} options (optional) An object containing properties which may override the options
4943      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
4944      * the most recently used options are reused).
4945      */
4946     reload : function(options){
4947         this.load(Roo.applyIf(options||{}, this.lastOptions));
4948     },
4949
4950     // private
4951     // Called as a callback by the Reader during a load operation.
4952     loadRecords : function(o, options, success){
4953         if(!o || success === false){
4954             if(success !== false){
4955                 this.fireEvent("load", this, [], options);
4956             }
4957             if(options.callback){
4958                 options.callback.call(options.scope || this, [], options, false);
4959             }
4960             return;
4961         }
4962         // if data returned failure - throw an exception.
4963         if (o.success === false) {
4964             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
4965             return;
4966         }
4967         var r = o.records, t = o.totalRecords || r.length;
4968         if(!options || options.add !== true){
4969             if(this.pruneModifiedRecords){
4970                 this.modified = [];
4971             }
4972             for(var i = 0, len = r.length; i < len; i++){
4973                 r[i].join(this);
4974             }
4975             if(this.snapshot){
4976                 this.data = this.snapshot;
4977                 delete this.snapshot;
4978             }
4979             this.data.clear();
4980             this.data.addAll(r);
4981             this.totalLength = t;
4982             this.applySort();
4983             this.fireEvent("datachanged", this);
4984         }else{
4985             this.totalLength = Math.max(t, this.data.length+r.length);
4986             this.add(r);
4987         }
4988         this.fireEvent("load", this, r, options);
4989         if(options.callback){
4990             options.callback.call(options.scope || this, r, options, true);
4991         }
4992     },
4993
4994     /**
4995      * Loads data from a passed data block. A Reader which understands the format of the data
4996      * must have been configured in the constructor.
4997      * @param {Object} data The data block from which to read the Records.  The format of the data expected
4998      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
4999      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5000      */
5001     loadData : function(o, append){
5002         var r = this.reader.readRecords(o);
5003         this.loadRecords(r, {add: append}, true);
5004     },
5005
5006     /**
5007      * Gets the number of cached records.
5008      * <p>
5009      * <em>If using paging, this may not be the total size of the dataset. If the data object
5010      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5011      * the data set size</em>
5012      */
5013     getCount : function(){
5014         return this.data.length || 0;
5015     },
5016
5017     /**
5018      * Gets the total number of records in the dataset as returned by the server.
5019      * <p>
5020      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5021      * the dataset size</em>
5022      */
5023     getTotalCount : function(){
5024         return this.totalLength || 0;
5025     },
5026
5027     /**
5028      * Returns the sort state of the Store as an object with two properties:
5029      * <pre><code>
5030  field {String} The name of the field by which the Records are sorted
5031  direction {String} The sort order, "ASC" or "DESC"
5032      * </code></pre>
5033      */
5034     getSortState : function(){
5035         return this.sortInfo;
5036     },
5037
5038     // private
5039     applySort : function(){
5040         if(this.sortInfo && !this.remoteSort){
5041             var s = this.sortInfo, f = s.field;
5042             var st = this.fields.get(f).sortType;
5043             var fn = function(r1, r2){
5044                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5045                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5046             };
5047             this.data.sort(s.direction, fn);
5048             if(this.snapshot && this.snapshot != this.data){
5049                 this.snapshot.sort(s.direction, fn);
5050             }
5051         }
5052     },
5053
5054     /**
5055      * Sets the default sort column and order to be used by the next load operation.
5056      * @param {String} fieldName The name of the field to sort by.
5057      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5058      */
5059     setDefaultSort : function(field, dir){
5060         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5061     },
5062
5063     /**
5064      * Sort the Records.
5065      * If remote sorting is used, the sort is performed on the server, and the cache is
5066      * reloaded. If local sorting is used, the cache is sorted internally.
5067      * @param {String} fieldName The name of the field to sort by.
5068      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5069      */
5070     sort : function(fieldName, dir){
5071         var f = this.fields.get(fieldName);
5072         if(!dir){
5073             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5074                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5075             }else{
5076                 dir = f.sortDir;
5077             }
5078         }
5079         this.sortToggle[f.name] = dir;
5080         this.sortInfo = {field: f.name, direction: dir};
5081         if(!this.remoteSort){
5082             this.applySort();
5083             this.fireEvent("datachanged", this);
5084         }else{
5085             this.load(this.lastOptions);
5086         }
5087     },
5088
5089     /**
5090      * Calls the specified function for each of the Records in the cache.
5091      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5092      * Returning <em>false</em> aborts and exits the iteration.
5093      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5094      */
5095     each : function(fn, scope){
5096         this.data.each(fn, scope);
5097     },
5098
5099     /**
5100      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5101      * (e.g., during paging).
5102      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5103      */
5104     getModifiedRecords : function(){
5105         return this.modified;
5106     },
5107
5108     // private
5109     createFilterFn : function(property, value, anyMatch){
5110         if(!value.exec){ // not a regex
5111             value = String(value);
5112             if(value.length == 0){
5113                 return false;
5114             }
5115             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5116         }
5117         return function(r){
5118             return value.test(r.data[property]);
5119         };
5120     },
5121
5122     /**
5123      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5124      * @param {String} property A field on your records
5125      * @param {Number} start The record index to start at (defaults to 0)
5126      * @param {Number} end The last record index to include (defaults to length - 1)
5127      * @return {Number} The sum
5128      */
5129     sum : function(property, start, end){
5130         var rs = this.data.items, v = 0;
5131         start = start || 0;
5132         end = (end || end === 0) ? end : rs.length-1;
5133
5134         for(var i = start; i <= end; i++){
5135             v += (rs[i].data[property] || 0);
5136         }
5137         return v;
5138     },
5139
5140     /**
5141      * Filter the records by a specified property.
5142      * @param {String} field A field on your records
5143      * @param {String/RegExp} value Either a string that the field
5144      * should start with or a RegExp to test against the field
5145      * @param {Boolean} anyMatch True to match any part not just the beginning
5146      */
5147     filter : function(property, value, anyMatch){
5148         var fn = this.createFilterFn(property, value, anyMatch);
5149         return fn ? this.filterBy(fn) : this.clearFilter();
5150     },
5151
5152     /**
5153      * Filter by a function. The specified function will be called with each
5154      * record in this data source. If the function returns true the record is included,
5155      * otherwise it is filtered.
5156      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5157      * @param {Object} scope (optional) The scope of the function (defaults to this)
5158      */
5159     filterBy : function(fn, scope){
5160         this.snapshot = this.snapshot || this.data;
5161         this.data = this.queryBy(fn, scope||this);
5162         this.fireEvent("datachanged", this);
5163     },
5164
5165     /**
5166      * Query the records by a specified property.
5167      * @param {String} field A field on your records
5168      * @param {String/RegExp} value Either a string that the field
5169      * should start with or a RegExp to test against the field
5170      * @param {Boolean} anyMatch True to match any part not just the beginning
5171      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5172      */
5173     query : function(property, value, anyMatch){
5174         var fn = this.createFilterFn(property, value, anyMatch);
5175         return fn ? this.queryBy(fn) : this.data.clone();
5176     },
5177
5178     /**
5179      * Query by a function. The specified function will be called with each
5180      * record in this data source. If the function returns true the record is included
5181      * in the results.
5182      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5183      * @param {Object} scope (optional) The scope of the function (defaults to this)
5184       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5185      **/
5186     queryBy : function(fn, scope){
5187         var data = this.snapshot || this.data;
5188         return data.filterBy(fn, scope||this);
5189     },
5190
5191     /**
5192      * Collects unique values for a particular dataIndex from this store.
5193      * @param {String} dataIndex The property to collect
5194      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5195      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5196      * @return {Array} An array of the unique values
5197      **/
5198     collect : function(dataIndex, allowNull, bypassFilter){
5199         var d = (bypassFilter === true && this.snapshot) ?
5200                 this.snapshot.items : this.data.items;
5201         var v, sv, r = [], l = {};
5202         for(var i = 0, len = d.length; i < len; i++){
5203             v = d[i].data[dataIndex];
5204             sv = String(v);
5205             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5206                 l[sv] = true;
5207                 r[r.length] = v;
5208             }
5209         }
5210         return r;
5211     },
5212
5213     /**
5214      * Revert to a view of the Record cache with no filtering applied.
5215      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5216      */
5217     clearFilter : function(suppressEvent){
5218         if(this.snapshot && this.snapshot != this.data){
5219             this.data = this.snapshot;
5220             delete this.snapshot;
5221             if(suppressEvent !== true){
5222                 this.fireEvent("datachanged", this);
5223             }
5224         }
5225     },
5226
5227     // private
5228     afterEdit : function(record){
5229         if(this.modified.indexOf(record) == -1){
5230             this.modified.push(record);
5231         }
5232         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5233     },
5234
5235     // private
5236     afterReject : function(record){
5237         this.modified.remove(record);
5238         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5239     },
5240
5241     // private
5242     afterCommit : function(record){
5243         this.modified.remove(record);
5244         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5245     },
5246
5247     /**
5248      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5249      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5250      */
5251     commitChanges : function(){
5252         var m = this.modified.slice(0);
5253         this.modified = [];
5254         for(var i = 0, len = m.length; i < len; i++){
5255             m[i].commit();
5256         }
5257     },
5258
5259     /**
5260      * Cancel outstanding changes on all changed records.
5261      */
5262     rejectChanges : function(){
5263         var m = this.modified.slice(0);
5264         this.modified = [];
5265         for(var i = 0, len = m.length; i < len; i++){
5266             m[i].reject();
5267         }
5268     },
5269
5270     onMetaChange : function(meta, rtype, o){
5271         this.recordType = rtype;
5272         this.fields = rtype.prototype.fields;
5273         delete this.snapshot;
5274         this.sortInfo = meta.sortInfo;
5275         this.modified = [];
5276         this.fireEvent('metachange', this, this.reader.meta);
5277     }
5278 });/*
5279  * Based on:
5280  * Ext JS Library 1.1.1
5281  * Copyright(c) 2006-2007, Ext JS, LLC.
5282  *
5283  * Originally Released Under LGPL - original licence link has changed is not relivant.
5284  *
5285  * Fork - LGPL
5286  * <script type="text/javascript">
5287  */
5288
5289 /**
5290  * @class Roo.data.SimpleStore
5291  * @extends Roo.data.Store
5292  * Small helper class to make creating Stores from Array data easier.
5293  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5294  * @cfg {Array} fields An array of field definition objects, or field name strings.
5295  * @cfg {Array} data The multi-dimensional array of data
5296  * @constructor
5297  * @param {Object} config
5298  */
5299 Roo.data.SimpleStore = function(config){
5300     Roo.data.SimpleStore.superclass.constructor.call(this, {
5301         isLocal : true,
5302         reader: new Roo.data.ArrayReader({
5303                 id: config.id
5304             },
5305             Roo.data.Record.create(config.fields)
5306         ),
5307         proxy : new Roo.data.MemoryProxy(config.data)
5308     });
5309     this.load();
5310 };
5311 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5312  * Based on:
5313  * Ext JS Library 1.1.1
5314  * Copyright(c) 2006-2007, Ext JS, LLC.
5315  *
5316  * Originally Released Under LGPL - original licence link has changed is not relivant.
5317  *
5318  * Fork - LGPL
5319  * <script type="text/javascript">
5320  */
5321
5322 /**
5323 /**
5324  * @extends Roo.data.Store
5325  * @class Roo.data.JsonStore
5326  * Small helper class to make creating Stores for JSON data easier. <br/>
5327 <pre><code>
5328 var store = new Roo.data.JsonStore({
5329     url: 'get-images.php',
5330     root: 'images',
5331     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5332 });
5333 </code></pre>
5334  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5335  * JsonReader and HttpProxy (unless inline data is provided).</b>
5336  * @cfg {Array} fields An array of field definition objects, or field name strings.
5337  * @constructor
5338  * @param {Object} config
5339  */
5340 Roo.data.JsonStore = function(c){
5341     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5342         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5343         reader: new Roo.data.JsonReader(c, c.fields)
5344     }));
5345 };
5346 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5347  * Based on:
5348  * Ext JS Library 1.1.1
5349  * Copyright(c) 2006-2007, Ext JS, LLC.
5350  *
5351  * Originally Released Under LGPL - original licence link has changed is not relivant.
5352  *
5353  * Fork - LGPL
5354  * <script type="text/javascript">
5355  */
5356
5357  
5358 Roo.data.Field = function(config){
5359     if(typeof config == "string"){
5360         config = {name: config};
5361     }
5362     Roo.apply(this, config);
5363     
5364     if(!this.type){
5365         this.type = "auto";
5366     }
5367     
5368     var st = Roo.data.SortTypes;
5369     // named sortTypes are supported, here we look them up
5370     if(typeof this.sortType == "string"){
5371         this.sortType = st[this.sortType];
5372     }
5373     
5374     // set default sortType for strings and dates
5375     if(!this.sortType){
5376         switch(this.type){
5377             case "string":
5378                 this.sortType = st.asUCString;
5379                 break;
5380             case "date":
5381                 this.sortType = st.asDate;
5382                 break;
5383             default:
5384                 this.sortType = st.none;
5385         }
5386     }
5387
5388     // define once
5389     var stripRe = /[\$,%]/g;
5390
5391     // prebuilt conversion function for this field, instead of
5392     // switching every time we're reading a value
5393     if(!this.convert){
5394         var cv, dateFormat = this.dateFormat;
5395         switch(this.type){
5396             case "":
5397             case "auto":
5398             case undefined:
5399                 cv = function(v){ return v; };
5400                 break;
5401             case "string":
5402                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5403                 break;
5404             case "int":
5405                 cv = function(v){
5406                     return v !== undefined && v !== null && v !== '' ?
5407                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5408                     };
5409                 break;
5410             case "float":
5411                 cv = function(v){
5412                     return v !== undefined && v !== null && v !== '' ?
5413                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5414                     };
5415                 break;
5416             case "bool":
5417             case "boolean":
5418                 cv = function(v){ return v === true || v === "true" || v == 1; };
5419                 break;
5420             case "date":
5421                 cv = function(v){
5422                     if(!v){
5423                         return '';
5424                     }
5425                     if(v instanceof Date){
5426                         return v;
5427                     }
5428                     if(dateFormat){
5429                         if(dateFormat == "timestamp"){
5430                             return new Date(v*1000);
5431                         }
5432                         return Date.parseDate(v, dateFormat);
5433                     }
5434                     var parsed = Date.parse(v);
5435                     return parsed ? new Date(parsed) : null;
5436                 };
5437              break;
5438             
5439         }
5440         this.convert = cv;
5441     }
5442 };
5443
5444 Roo.data.Field.prototype = {
5445     dateFormat: null,
5446     defaultValue: "",
5447     mapping: null,
5448     sortType : null,
5449     sortDir : "ASC"
5450 };/*
5451  * Based on:
5452  * Ext JS Library 1.1.1
5453  * Copyright(c) 2006-2007, Ext JS, LLC.
5454  *
5455  * Originally Released Under LGPL - original licence link has changed is not relivant.
5456  *
5457  * Fork - LGPL
5458  * <script type="text/javascript">
5459  */
5460  
5461 // Base class for reading structured data from a data source.  This class is intended to be
5462 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5463
5464 /**
5465  * @class Roo.data.DataReader
5466  * Base class for reading structured data from a data source.  This class is intended to be
5467  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5468  */
5469
5470 Roo.data.DataReader = function(meta, recordType){
5471     
5472     this.meta = meta;
5473     
5474     this.recordType = recordType instanceof Array ? 
5475         Roo.data.Record.create(recordType) : recordType;
5476 };
5477
5478 Roo.data.DataReader.prototype = {
5479      /**
5480      * Create an empty record
5481      * @param {Object} data (optional) - overlay some values
5482      * @return {Roo.data.Record} record created.
5483      */
5484     newRow :  function(d) {
5485         var da =  {};
5486         this.recordType.prototype.fields.each(function(c) {
5487             switch( c.type) {
5488                 case 'int' : da[c.name] = 0; break;
5489                 case 'date' : da[c.name] = new Date(); break;
5490                 case 'float' : da[c.name] = 0.0; break;
5491                 case 'boolean' : da[c.name] = false; break;
5492                 default : da[c.name] = ""; break;
5493             }
5494             
5495         });
5496         return new this.recordType(Roo.apply(da, d));
5497     }
5498     
5499 };/*
5500  * Based on:
5501  * Ext JS Library 1.1.1
5502  * Copyright(c) 2006-2007, Ext JS, LLC.
5503  *
5504  * Originally Released Under LGPL - original licence link has changed is not relivant.
5505  *
5506  * Fork - LGPL
5507  * <script type="text/javascript">
5508  */
5509
5510 /**
5511  * @class Roo.data.DataProxy
5512  * @extends Roo.data.Observable
5513  * This class is an abstract base class for implementations which provide retrieval of
5514  * unformatted data objects.<br>
5515  * <p>
5516  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5517  * (of the appropriate type which knows how to parse the data object) to provide a block of
5518  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5519  * <p>
5520  * Custom implementations must implement the load method as described in
5521  * {@link Roo.data.HttpProxy#load}.
5522  */
5523 Roo.data.DataProxy = function(){
5524     this.addEvents({
5525         /**
5526          * @event beforeload
5527          * Fires before a network request is made to retrieve a data object.
5528          * @param {Object} This DataProxy object.
5529          * @param {Object} params The params parameter to the load function.
5530          */
5531         beforeload : true,
5532         /**
5533          * @event load
5534          * Fires before the load method's callback is called.
5535          * @param {Object} This DataProxy object.
5536          * @param {Object} o The data object.
5537          * @param {Object} arg The callback argument object passed to the load function.
5538          */
5539         load : true,
5540         /**
5541          * @event loadexception
5542          * Fires if an Exception occurs during data retrieval.
5543          * @param {Object} This DataProxy object.
5544          * @param {Object} o The data object.
5545          * @param {Object} arg The callback argument object passed to the load function.
5546          * @param {Object} e The Exception.
5547          */
5548         loadexception : true
5549     });
5550     Roo.data.DataProxy.superclass.constructor.call(this);
5551 };
5552
5553 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5554
5555     /**
5556      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5557      */
5558 /*
5559  * Based on:
5560  * Ext JS Library 1.1.1
5561  * Copyright(c) 2006-2007, Ext JS, LLC.
5562  *
5563  * Originally Released Under LGPL - original licence link has changed is not relivant.
5564  *
5565  * Fork - LGPL
5566  * <script type="text/javascript">
5567  */
5568 /**
5569  * @class Roo.data.MemoryProxy
5570  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5571  * to the Reader when its load method is called.
5572  * @constructor
5573  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5574  */
5575 Roo.data.MemoryProxy = function(data){
5576     if (data.data) {
5577         data = data.data;
5578     }
5579     Roo.data.MemoryProxy.superclass.constructor.call(this);
5580     this.data = data;
5581 };
5582
5583 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5584     /**
5585      * Load data from the requested source (in this case an in-memory
5586      * data object passed to the constructor), read the data object into
5587      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5588      * process that block using the passed callback.
5589      * @param {Object} params This parameter is not used by the MemoryProxy class.
5590      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5591      * object into a block of Roo.data.Records.
5592      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5593      * The function must be passed <ul>
5594      * <li>The Record block object</li>
5595      * <li>The "arg" argument from the load function</li>
5596      * <li>A boolean success indicator</li>
5597      * </ul>
5598      * @param {Object} scope The scope in which to call the callback
5599      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5600      */
5601     load : function(params, reader, callback, scope, arg){
5602         params = params || {};
5603         var result;
5604         try {
5605             result = reader.readRecords(this.data);
5606         }catch(e){
5607             this.fireEvent("loadexception", this, arg, null, e);
5608             callback.call(scope, null, arg, false);
5609             return;
5610         }
5611         callback.call(scope, result, arg, true);
5612     },
5613     
5614     // private
5615     update : function(params, records){
5616         
5617     }
5618 });/*
5619  * Based on:
5620  * Ext JS Library 1.1.1
5621  * Copyright(c) 2006-2007, Ext JS, LLC.
5622  *
5623  * Originally Released Under LGPL - original licence link has changed is not relivant.
5624  *
5625  * Fork - LGPL
5626  * <script type="text/javascript">
5627  */
5628 /**
5629  * @class Roo.data.HttpProxy
5630  * @extends Roo.data.DataProxy
5631  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5632  * configured to reference a certain URL.<br><br>
5633  * <p>
5634  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5635  * from which the running page was served.<br><br>
5636  * <p>
5637  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5638  * <p>
5639  * Be aware that to enable the browser to parse an XML document, the server must set
5640  * the Content-Type header in the HTTP response to "text/xml".
5641  * @constructor
5642  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5643  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5644  * will be used to make the request.
5645  */
5646 Roo.data.HttpProxy = function(conn){
5647     Roo.data.HttpProxy.superclass.constructor.call(this);
5648     // is conn a conn config or a real conn?
5649     this.conn = conn;
5650     this.useAjax = !conn || !conn.events;
5651   
5652 };
5653
5654 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5655     // thse are take from connection...
5656     
5657     /**
5658      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5659      */
5660     /**
5661      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5662      * extra parameters to each request made by this object. (defaults to undefined)
5663      */
5664     /**
5665      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5666      *  to each request made by this object. (defaults to undefined)
5667      */
5668     /**
5669      * @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)
5670      */
5671     /**
5672      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5673      */
5674      /**
5675      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5676      * @type Boolean
5677      */
5678   
5679
5680     /**
5681      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5682      * @type Boolean
5683      */
5684     /**
5685      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5686      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5687      * a finer-grained basis than the DataProxy events.
5688      */
5689     getConnection : function(){
5690         return this.useAjax ? Roo.Ajax : this.conn;
5691     },
5692
5693     /**
5694      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5695      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5696      * process that block using the passed callback.
5697      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5698      * for the request to the remote server.
5699      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5700      * object into a block of Roo.data.Records.
5701      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5702      * The function must be passed <ul>
5703      * <li>The Record block object</li>
5704      * <li>The "arg" argument from the load function</li>
5705      * <li>A boolean success indicator</li>
5706      * </ul>
5707      * @param {Object} scope The scope in which to call the callback
5708      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5709      */
5710     load : function(params, reader, callback, scope, arg){
5711         if(this.fireEvent("beforeload", this, params) !== false){
5712             var  o = {
5713                 params : params || {},
5714                 request: {
5715                     callback : callback,
5716                     scope : scope,
5717                     arg : arg
5718                 },
5719                 reader: reader,
5720                 callback : this.loadResponse,
5721                 scope: this
5722             };
5723             if(this.useAjax){
5724                 Roo.applyIf(o, this.conn);
5725                 if(this.activeRequest){
5726                     Roo.Ajax.abort(this.activeRequest);
5727                 }
5728                 this.activeRequest = Roo.Ajax.request(o);
5729             }else{
5730                 this.conn.request(o);
5731             }
5732         }else{
5733             callback.call(scope||this, null, arg, false);
5734         }
5735     },
5736
5737     // private
5738     loadResponse : function(o, success, response){
5739         delete this.activeRequest;
5740         if(!success){
5741             this.fireEvent("loadexception", this, o, response);
5742             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5743             return;
5744         }
5745         var result;
5746         try {
5747             result = o.reader.read(response);
5748         }catch(e){
5749             this.fireEvent("loadexception", this, o, response, e);
5750             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5751             return;
5752         }
5753         
5754         this.fireEvent("load", this, o, o.request.arg);
5755         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5756     },
5757
5758     // private
5759     update : function(dataSet){
5760
5761     },
5762
5763     // private
5764     updateResponse : function(dataSet){
5765
5766     }
5767 });/*
5768  * Based on:
5769  * Ext JS Library 1.1.1
5770  * Copyright(c) 2006-2007, Ext JS, LLC.
5771  *
5772  * Originally Released Under LGPL - original licence link has changed is not relivant.
5773  *
5774  * Fork - LGPL
5775  * <script type="text/javascript">
5776  */
5777
5778 /**
5779  * @class Roo.data.ScriptTagProxy
5780  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5781  * other than the originating domain of the running page.<br><br>
5782  * <p>
5783  * <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
5784  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5785  * <p>
5786  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5787  * source code that is used as the source inside a &lt;script> tag.<br><br>
5788  * <p>
5789  * In order for the browser to process the returned data, the server must wrap the data object
5790  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5791  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5792  * depending on whether the callback name was passed:
5793  * <p>
5794  * <pre><code>
5795 boolean scriptTag = false;
5796 String cb = request.getParameter("callback");
5797 if (cb != null) {
5798     scriptTag = true;
5799     response.setContentType("text/javascript");
5800 } else {
5801     response.setContentType("application/x-json");
5802 }
5803 Writer out = response.getWriter();
5804 if (scriptTag) {
5805     out.write(cb + "(");
5806 }
5807 out.print(dataBlock.toJsonString());
5808 if (scriptTag) {
5809     out.write(");");
5810 }
5811 </pre></code>
5812  *
5813  * @constructor
5814  * @param {Object} config A configuration object.
5815  */
5816 Roo.data.ScriptTagProxy = function(config){
5817     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5818     Roo.apply(this, config);
5819     this.head = document.getElementsByTagName("head")[0];
5820 };
5821
5822 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5823
5824 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5825     /**
5826      * @cfg {String} url The URL from which to request the data object.
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5830      */
5831     timeout : 30000,
5832     /**
5833      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5834      * the server the name of the callback function set up by the load call to process the returned data object.
5835      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5836      * javascript output which calls this named function passing the data object as its only parameter.
5837      */
5838     callbackParam : "callback",
5839     /**
5840      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5841      * name to the request.
5842      */
5843     nocache : true,
5844
5845     /**
5846      * Load data from the configured URL, read the data object into
5847      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5848      * process that block using the passed callback.
5849      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5850      * for the request to the remote server.
5851      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5852      * object into a block of Roo.data.Records.
5853      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5854      * The function must be passed <ul>
5855      * <li>The Record block object</li>
5856      * <li>The "arg" argument from the load function</li>
5857      * <li>A boolean success indicator</li>
5858      * </ul>
5859      * @param {Object} scope The scope in which to call the callback
5860      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5861      */
5862     load : function(params, reader, callback, scope, arg){
5863         if(this.fireEvent("beforeload", this, params) !== false){
5864
5865             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5866
5867             var url = this.url;
5868             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5869             if(this.nocache){
5870                 url += "&_dc=" + (new Date().getTime());
5871             }
5872             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5873             var trans = {
5874                 id : transId,
5875                 cb : "stcCallback"+transId,
5876                 scriptId : "stcScript"+transId,
5877                 params : params,
5878                 arg : arg,
5879                 url : url,
5880                 callback : callback,
5881                 scope : scope,
5882                 reader : reader
5883             };
5884             var conn = this;
5885
5886             window[trans.cb] = function(o){
5887                 conn.handleResponse(o, trans);
5888             };
5889
5890             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5891
5892             if(this.autoAbort !== false){
5893                 this.abort();
5894             }
5895
5896             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5897
5898             var script = document.createElement("script");
5899             script.setAttribute("src", url);
5900             script.setAttribute("type", "text/javascript");
5901             script.setAttribute("id", trans.scriptId);
5902             this.head.appendChild(script);
5903
5904             this.trans = trans;
5905         }else{
5906             callback.call(scope||this, null, arg, false);
5907         }
5908     },
5909
5910     // private
5911     isLoading : function(){
5912         return this.trans ? true : false;
5913     },
5914
5915     /**
5916      * Abort the current server request.
5917      */
5918     abort : function(){
5919         if(this.isLoading()){
5920             this.destroyTrans(this.trans);
5921         }
5922     },
5923
5924     // private
5925     destroyTrans : function(trans, isLoaded){
5926         this.head.removeChild(document.getElementById(trans.scriptId));
5927         clearTimeout(trans.timeoutId);
5928         if(isLoaded){
5929             window[trans.cb] = undefined;
5930             try{
5931                 delete window[trans.cb];
5932             }catch(e){}
5933         }else{
5934             // if hasn't been loaded, wait for load to remove it to prevent script error
5935             window[trans.cb] = function(){
5936                 window[trans.cb] = undefined;
5937                 try{
5938                     delete window[trans.cb];
5939                 }catch(e){}
5940             };
5941         }
5942     },
5943
5944     // private
5945     handleResponse : function(o, trans){
5946         this.trans = false;
5947         this.destroyTrans(trans, true);
5948         var result;
5949         try {
5950             result = trans.reader.readRecords(o);
5951         }catch(e){
5952             this.fireEvent("loadexception", this, o, trans.arg, e);
5953             trans.callback.call(trans.scope||window, null, trans.arg, false);
5954             return;
5955         }
5956         this.fireEvent("load", this, o, trans.arg);
5957         trans.callback.call(trans.scope||window, result, trans.arg, true);
5958     },
5959
5960     // private
5961     handleFailure : function(trans){
5962         this.trans = false;
5963         this.destroyTrans(trans, false);
5964         this.fireEvent("loadexception", this, null, trans.arg);
5965         trans.callback.call(trans.scope||window, null, trans.arg, false);
5966     }
5967 });/*
5968  * Based on:
5969  * Ext JS Library 1.1.1
5970  * Copyright(c) 2006-2007, Ext JS, LLC.
5971  *
5972  * Originally Released Under LGPL - original licence link has changed is not relivant.
5973  *
5974  * Fork - LGPL
5975  * <script type="text/javascript">
5976  */
5977
5978 /**
5979  * @class Roo.data.JsonReader
5980  * @extends Roo.data.DataReader
5981  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
5982  * based on mappings in a provided Roo.data.Record constructor.
5983  * <p>
5984  * Example code:
5985  * <pre><code>
5986 var RecordDef = Roo.data.Record.create([
5987     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
5988     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
5989 ]);
5990 var myReader = new Roo.data.JsonReader({
5991     totalProperty: "results",    // The property which contains the total dataset size (optional)
5992     root: "rows",                // The property which contains an Array of row objects
5993     id: "id"                     // The property within each row object that provides an ID for the record (optional)
5994 }, RecordDef);
5995 </code></pre>
5996  * <p>
5997  * This would consume a JSON file like this:
5998  * <pre><code>
5999 { 'results': 2, 'rows': [
6000     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6001     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6002 }
6003 </code></pre>
6004  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6005  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6006  * paged from the remote server.
6007  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6008  * @cfg {String} root name of the property which contains the Array of row objects.
6009  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6010  * @constructor
6011  * Create a new JsonReader
6012  * @param {Object} meta Metadata configuration options
6013  * @param {Object} recordType Either an Array of field definition objects,
6014  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6015  */
6016 Roo.data.JsonReader = function(meta, recordType){
6017     
6018     meta = meta || {};
6019     // set some defaults:
6020     Roo.applyIf(meta, {
6021         totalProperty: 'total',
6022         successProperty : 'success',
6023         root : 'data',
6024         id : 'id'
6025     });
6026     
6027     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6028 };
6029 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6030     /**
6031      * This method is only used by a DataProxy which has retrieved data from a remote server.
6032      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6033      * @return {Object} data A data block which is used by an Roo.data.Store object as
6034      * a cache of Roo.data.Records.
6035      */
6036     read : function(response){
6037         var json = response.responseText;
6038         /* eval:var:o */
6039         var o = eval("("+json+")");
6040         if(!o) {
6041             throw {message: "JsonReader.read: Json object not found"};
6042         }
6043         
6044         if(o.metaData){
6045             delete this.ef;
6046             this.meta = o.metaData;
6047             this.recordType = Roo.data.Record.create(o.metaData.fields);
6048             this.onMetaChange(this.meta, this.recordType, o);
6049         }
6050         return this.readRecords(o);
6051     },
6052
6053     // private function a store will implement
6054     onMetaChange : function(meta, recordType, o){
6055
6056     },
6057
6058     /**
6059          * @ignore
6060          */
6061     simpleAccess: function(obj, subsc) {
6062         return obj[subsc];
6063     },
6064
6065         /**
6066          * @ignore
6067          */
6068     getJsonAccessor: function(){
6069         var re = /[\[\.]/;
6070         return function(expr) {
6071             try {
6072                 return(re.test(expr))
6073                     ? new Function("obj", "return obj." + expr)
6074                     : function(obj){
6075                         return obj[expr];
6076                     };
6077             } catch(e){}
6078             return Roo.emptyFn;
6079         };
6080     }(),
6081
6082     /**
6083      * Create a data block containing Roo.data.Records from an XML document.
6084      * @param {Object} o An object which contains an Array of row objects in the property specified
6085      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6086      * which contains the total size of the dataset.
6087      * @return {Object} data A data block which is used by an Roo.data.Store object as
6088      * a cache of Roo.data.Records.
6089      */
6090     readRecords : function(o){
6091         /**
6092          * After any data loads, the raw JSON data is available for further custom processing.
6093          * @type Object
6094          */
6095         this.jsonData = o;
6096         var s = this.meta, Record = this.recordType,
6097             f = Record.prototype.fields, fi = f.items, fl = f.length;
6098
6099 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6100         if (!this.ef) {
6101             if(s.totalProperty) {
6102                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6103                 }
6104                 if(s.successProperty) {
6105                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6106                 }
6107                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6108                 if (s.id) {
6109                         var g = this.getJsonAccessor(s.id);
6110                         this.getId = function(rec) {
6111                                 var r = g(rec);
6112                                 return (r === undefined || r === "") ? null : r;
6113                         };
6114                 } else {
6115                         this.getId = function(){return null;};
6116                 }
6117             this.ef = [];
6118             for(var i = 0; i < fl; i++){
6119                 f = fi[i];
6120                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6121                 this.ef[i] = this.getJsonAccessor(map);
6122             }
6123         }
6124
6125         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6126         if(s.totalProperty){
6127             var v = parseInt(this.getTotal(o), 10);
6128             if(!isNaN(v)){
6129                 totalRecords = v;
6130             }
6131         }
6132         if(s.successProperty){
6133             var v = this.getSuccess(o);
6134             if(v === false || v === 'false'){
6135                 success = false;
6136             }
6137         }
6138         var records = [];
6139             for(var i = 0; i < c; i++){
6140                     var n = root[i];
6141                 var values = {};
6142                 var id = this.getId(n);
6143                 for(var j = 0; j < fl; j++){
6144                     f = fi[j];
6145                 var v = this.ef[j](n);
6146                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6147                 }
6148                 var record = new Record(values, id);
6149                 record.json = n;
6150                 records[i] = record;
6151             }
6152             return {
6153                 success : success,
6154                 records : records,
6155                 totalRecords : totalRecords
6156             };
6157     }
6158 });/*
6159  * Based on:
6160  * Ext JS Library 1.1.1
6161  * Copyright(c) 2006-2007, Ext JS, LLC.
6162  *
6163  * Originally Released Under LGPL - original licence link has changed is not relivant.
6164  *
6165  * Fork - LGPL
6166  * <script type="text/javascript">
6167  */
6168
6169 /**
6170  * @class Roo.data.XmlReader
6171  * @extends Roo.data.DataReader
6172  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6173  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6174  * <p>
6175  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6176  * header in the HTTP response must be set to "text/xml".</em>
6177  * <p>
6178  * Example code:
6179  * <pre><code>
6180 var RecordDef = Roo.data.Record.create([
6181    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6182    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6183 ]);
6184 var myReader = new Roo.data.XmlReader({
6185    totalRecords: "results", // The element which contains the total dataset size (optional)
6186    record: "row",           // The repeated element which contains row information
6187    id: "id"                 // The element within the row that provides an ID for the record (optional)
6188 }, RecordDef);
6189 </code></pre>
6190  * <p>
6191  * This would consume an XML file like this:
6192  * <pre><code>
6193 &lt;?xml?>
6194 &lt;dataset>
6195  &lt;results>2&lt;/results>
6196  &lt;row>
6197    &lt;id>1&lt;/id>
6198    &lt;name>Bill&lt;/name>
6199    &lt;occupation>Gardener&lt;/occupation>
6200  &lt;/row>
6201  &lt;row>
6202    &lt;id>2&lt;/id>
6203    &lt;name>Ben&lt;/name>
6204    &lt;occupation>Horticulturalist&lt;/occupation>
6205  &lt;/row>
6206 &lt;/dataset>
6207 </code></pre>
6208  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6209  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6210  * paged from the remote server.
6211  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6212  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6213  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6214  * a record identifier value.
6215  * @constructor
6216  * Create a new XmlReader
6217  * @param {Object} meta Metadata configuration options
6218  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6219  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6220  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6221  */
6222 Roo.data.XmlReader = function(meta, recordType){
6223     meta = meta || {};
6224     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6225 };
6226 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6227     /**
6228      * This method is only used by a DataProxy which has retrieved data from a remote server.
6229          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6230          * to contain a method called 'responseXML' that returns an XML document object.
6231      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6232      * a cache of Roo.data.Records.
6233      */
6234     read : function(response){
6235         var doc = response.responseXML;
6236         if(!doc) {
6237             throw {message: "XmlReader.read: XML Document not available"};
6238         }
6239         return this.readRecords(doc);
6240     },
6241
6242     /**
6243      * Create a data block containing Roo.data.Records from an XML document.
6244          * @param {Object} doc A parsed XML document.
6245      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6246      * a cache of Roo.data.Records.
6247      */
6248     readRecords : function(doc){
6249         /**
6250          * After any data loads/reads, the raw XML Document is available for further custom processing.
6251          * @type XMLDocument
6252          */
6253         this.xmlData = doc;
6254         var root = doc.documentElement || doc;
6255         var q = Roo.DomQuery;
6256         var recordType = this.recordType, fields = recordType.prototype.fields;
6257         var sid = this.meta.id;
6258         var totalRecords = 0, success = true;
6259         if(this.meta.totalRecords){
6260             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6261         }
6262         
6263         if(this.meta.success){
6264             var sv = q.selectValue(this.meta.success, root, true);
6265             success = sv !== false && sv !== 'false';
6266         }
6267         var records = [];
6268         var ns = q.select(this.meta.record, root);
6269         for(var i = 0, len = ns.length; i < len; i++) {
6270                 var n = ns[i];
6271                 var values = {};
6272                 var id = sid ? q.selectValue(sid, n) : undefined;
6273                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6274                     var f = fields.items[j];
6275                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6276                     v = f.convert(v);
6277                     values[f.name] = v;
6278                 }
6279                 var record = new recordType(values, id);
6280                 record.node = n;
6281                 records[records.length] = record;
6282             }
6283
6284             return {
6285                 success : success,
6286                 records : records,
6287                 totalRecords : totalRecords || records.length
6288             };
6289     }
6290 });/*
6291  * Based on:
6292  * Ext JS Library 1.1.1
6293  * Copyright(c) 2006-2007, Ext JS, LLC.
6294  *
6295  * Originally Released Under LGPL - original licence link has changed is not relivant.
6296  *
6297  * Fork - LGPL
6298  * <script type="text/javascript">
6299  */
6300
6301 /**
6302  * @class Roo.data.ArrayReader
6303  * @extends Roo.data.DataReader
6304  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6305  * Each element of that Array represents a row of data fields. The
6306  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6307  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6308  * <p>
6309  * Example code:.
6310  * <pre><code>
6311 var RecordDef = Roo.data.Record.create([
6312     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6313     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6314 ]);
6315 var myReader = new Roo.data.ArrayReader({
6316     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6317 }, RecordDef);
6318 </code></pre>
6319  * <p>
6320  * This would consume an Array like this:
6321  * <pre><code>
6322 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6323   </code></pre>
6324  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6325  * @constructor
6326  * Create a new JsonReader
6327  * @param {Object} meta Metadata configuration options.
6328  * @param {Object} recordType Either an Array of field definition objects
6329  * as specified to {@link Roo.data.Record#create},
6330  * or an {@link Roo.data.Record} object
6331  * created using {@link Roo.data.Record#create}.
6332  */
6333 Roo.data.ArrayReader = function(meta, recordType){
6334     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6335 };
6336
6337 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6338     /**
6339      * Create a data block containing Roo.data.Records from an XML document.
6340      * @param {Object} o An Array of row objects which represents the dataset.
6341      * @return {Object} data A data block which is used by an Roo.data.Store object as
6342      * a cache of Roo.data.Records.
6343      */
6344     readRecords : function(o){
6345         var sid = this.meta ? this.meta.id : null;
6346         var recordType = this.recordType, fields = recordType.prototype.fields;
6347         var records = [];
6348         var root = o;
6349             for(var i = 0; i < root.length; i++){
6350                     var n = root[i];
6351                 var values = {};
6352                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6353                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6354                 var f = fields.items[j];
6355                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6356                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6357                 v = f.convert(v);
6358                 values[f.name] = v;
6359             }
6360                 var record = new recordType(values, id);
6361                 record.json = n;
6362                 records[records.length] = record;
6363             }
6364             return {
6365                 records : records,
6366                 totalRecords : records.length
6367             };
6368     }
6369 });/*
6370  * Based on:
6371  * Ext JS Library 1.1.1
6372  * Copyright(c) 2006-2007, Ext JS, LLC.
6373  *
6374  * Originally Released Under LGPL - original licence link has changed is not relivant.
6375  *
6376  * Fork - LGPL
6377  * <script type="text/javascript">
6378  */
6379
6380
6381 /**
6382  * @class Roo.data.Tree
6383  * @extends Roo.util.Observable
6384  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6385  * in the tree have most standard DOM functionality.
6386  * @constructor
6387  * @param {Node} root (optional) The root node
6388  */
6389 Roo.data.Tree = function(root){
6390    this.nodeHash = {};
6391    /**
6392     * The root node for this tree
6393     * @type Node
6394     */
6395    this.root = null;
6396    if(root){
6397        this.setRootNode(root);
6398    }
6399    this.addEvents({
6400        /**
6401         * @event append
6402         * Fires when a new child node is appended to a node in this tree.
6403         * @param {Tree} tree The owner tree
6404         * @param {Node} parent The parent node
6405         * @param {Node} node The newly appended node
6406         * @param {Number} index The index of the newly appended node
6407         */
6408        "append" : true,
6409        /**
6410         * @event remove
6411         * Fires when a child node is removed from a node in this tree.
6412         * @param {Tree} tree The owner tree
6413         * @param {Node} parent The parent node
6414         * @param {Node} node The child node removed
6415         */
6416        "remove" : true,
6417        /**
6418         * @event move
6419         * Fires when a node is moved to a new location in the tree
6420         * @param {Tree} tree The owner tree
6421         * @param {Node} node The node moved
6422         * @param {Node} oldParent The old parent of this node
6423         * @param {Node} newParent The new parent of this node
6424         * @param {Number} index The index it was moved to
6425         */
6426        "move" : true,
6427        /**
6428         * @event insert
6429         * Fires when a new child node is inserted in a node in this tree.
6430         * @param {Tree} tree The owner tree
6431         * @param {Node} parent The parent node
6432         * @param {Node} node The child node inserted
6433         * @param {Node} refNode The child node the node was inserted before
6434         */
6435        "insert" : true,
6436        /**
6437         * @event beforeappend
6438         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6439         * @param {Tree} tree The owner tree
6440         * @param {Node} parent The parent node
6441         * @param {Node} node The child node to be appended
6442         */
6443        "beforeappend" : true,
6444        /**
6445         * @event beforeremove
6446         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6447         * @param {Tree} tree The owner tree
6448         * @param {Node} parent The parent node
6449         * @param {Node} node The child node to be removed
6450         */
6451        "beforeremove" : true,
6452        /**
6453         * @event beforemove
6454         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6455         * @param {Tree} tree The owner tree
6456         * @param {Node} node The node being moved
6457         * @param {Node} oldParent The parent of the node
6458         * @param {Node} newParent The new parent the node is moving to
6459         * @param {Number} index The index it is being moved to
6460         */
6461        "beforemove" : true,
6462        /**
6463         * @event beforeinsert
6464         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6465         * @param {Tree} tree The owner tree
6466         * @param {Node} parent The parent node
6467         * @param {Node} node The child node to be inserted
6468         * @param {Node} refNode The child node the node is being inserted before
6469         */
6470        "beforeinsert" : true
6471    });
6472
6473     Roo.data.Tree.superclass.constructor.call(this);
6474 };
6475
6476 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6477     pathSeparator: "/",
6478
6479     proxyNodeEvent : function(){
6480         return this.fireEvent.apply(this, arguments);
6481     },
6482
6483     /**
6484      * Returns the root node for this tree.
6485      * @return {Node}
6486      */
6487     getRootNode : function(){
6488         return this.root;
6489     },
6490
6491     /**
6492      * Sets the root node for this tree.
6493      * @param {Node} node
6494      * @return {Node}
6495      */
6496     setRootNode : function(node){
6497         this.root = node;
6498         node.ownerTree = this;
6499         node.isRoot = true;
6500         this.registerNode(node);
6501         return node;
6502     },
6503
6504     /**
6505      * Gets a node in this tree by its id.
6506      * @param {String} id
6507      * @return {Node}
6508      */
6509     getNodeById : function(id){
6510         return this.nodeHash[id];
6511     },
6512
6513     registerNode : function(node){
6514         this.nodeHash[node.id] = node;
6515     },
6516
6517     unregisterNode : function(node){
6518         delete this.nodeHash[node.id];
6519     },
6520
6521     toString : function(){
6522         return "[Tree"+(this.id?" "+this.id:"")+"]";
6523     }
6524 });
6525
6526 /**
6527  * @class Roo.data.Node
6528  * @extends Roo.util.Observable
6529  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6530  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6531  * @constructor
6532  * @param {Object} attributes The attributes/config for the node
6533  */
6534 Roo.data.Node = function(attributes){
6535     /**
6536      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6537      * @type {Object}
6538      */
6539     this.attributes = attributes || {};
6540     this.leaf = this.attributes.leaf;
6541     /**
6542      * The node id. @type String
6543      */
6544     this.id = this.attributes.id;
6545     if(!this.id){
6546         this.id = Roo.id(null, "ynode-");
6547         this.attributes.id = this.id;
6548     }
6549     /**
6550      * All child nodes of this node. @type Array
6551      */
6552     this.childNodes = [];
6553     if(!this.childNodes.indexOf){ // indexOf is a must
6554         this.childNodes.indexOf = function(o){
6555             for(var i = 0, len = this.length; i < len; i++){
6556                 if(this[i] == o) return i;
6557             }
6558             return -1;
6559         };
6560     }
6561     /**
6562      * The parent node for this node. @type Node
6563      */
6564     this.parentNode = null;
6565     /**
6566      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6567      */
6568     this.firstChild = null;
6569     /**
6570      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6571      */
6572     this.lastChild = null;
6573     /**
6574      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6575      */
6576     this.previousSibling = null;
6577     /**
6578      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6579      */
6580     this.nextSibling = null;
6581
6582     this.addEvents({
6583        /**
6584         * @event append
6585         * Fires when a new child node is appended
6586         * @param {Tree} tree The owner tree
6587         * @param {Node} this This node
6588         * @param {Node} node The newly appended node
6589         * @param {Number} index The index of the newly appended node
6590         */
6591        "append" : true,
6592        /**
6593         * @event remove
6594         * Fires when a child node is removed
6595         * @param {Tree} tree The owner tree
6596         * @param {Node} this This node
6597         * @param {Node} node The removed node
6598         */
6599        "remove" : true,
6600        /**
6601         * @event move
6602         * Fires when this node is moved to a new location in the tree
6603         * @param {Tree} tree The owner tree
6604         * @param {Node} this This node
6605         * @param {Node} oldParent The old parent of this node
6606         * @param {Node} newParent The new parent of this node
6607         * @param {Number} index The index it was moved to
6608         */
6609        "move" : true,
6610        /**
6611         * @event insert
6612         * Fires when a new child node is inserted.
6613         * @param {Tree} tree The owner tree
6614         * @param {Node} this This node
6615         * @param {Node} node The child node inserted
6616         * @param {Node} refNode The child node the node was inserted before
6617         */
6618        "insert" : true,
6619        /**
6620         * @event beforeappend
6621         * Fires before a new child is appended, return false to cancel the append.
6622         * @param {Tree} tree The owner tree
6623         * @param {Node} this This node
6624         * @param {Node} node The child node to be appended
6625         */
6626        "beforeappend" : true,
6627        /**
6628         * @event beforeremove
6629         * Fires before a child is removed, return false to cancel the remove.
6630         * @param {Tree} tree The owner tree
6631         * @param {Node} this This node
6632         * @param {Node} node The child node to be removed
6633         */
6634        "beforeremove" : true,
6635        /**
6636         * @event beforemove
6637         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6638         * @param {Tree} tree The owner tree
6639         * @param {Node} this This node
6640         * @param {Node} oldParent The parent of this node
6641         * @param {Node} newParent The new parent this node is moving to
6642         * @param {Number} index The index it is being moved to
6643         */
6644        "beforemove" : true,
6645        /**
6646         * @event beforeinsert
6647         * Fires before a new child is inserted, return false to cancel the insert.
6648         * @param {Tree} tree The owner tree
6649         * @param {Node} this This node
6650         * @param {Node} node The child node to be inserted
6651         * @param {Node} refNode The child node the node is being inserted before
6652         */
6653        "beforeinsert" : true
6654    });
6655     this.listeners = this.attributes.listeners;
6656     Roo.data.Node.superclass.constructor.call(this);
6657 };
6658
6659 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6660     fireEvent : function(evtName){
6661         // first do standard event for this node
6662         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6663             return false;
6664         }
6665         // then bubble it up to the tree if the event wasn't cancelled
6666         var ot = this.getOwnerTree();
6667         if(ot){
6668             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6669                 return false;
6670             }
6671         }
6672         return true;
6673     },
6674
6675     /**
6676      * Returns true if this node is a leaf
6677      * @return {Boolean}
6678      */
6679     isLeaf : function(){
6680         return this.leaf === true;
6681     },
6682
6683     // private
6684     setFirstChild : function(node){
6685         this.firstChild = node;
6686     },
6687
6688     //private
6689     setLastChild : function(node){
6690         this.lastChild = node;
6691     },
6692
6693
6694     /**
6695      * Returns true if this node is the last child of its parent
6696      * @return {Boolean}
6697      */
6698     isLast : function(){
6699        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6700     },
6701
6702     /**
6703      * Returns true if this node is the first child of its parent
6704      * @return {Boolean}
6705      */
6706     isFirst : function(){
6707        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6708     },
6709
6710     hasChildNodes : function(){
6711         return !this.isLeaf() && this.childNodes.length > 0;
6712     },
6713
6714     /**
6715      * Insert node(s) as the last child node of this node.
6716      * @param {Node/Array} node The node or Array of nodes to append
6717      * @return {Node} The appended node if single append, or null if an array was passed
6718      */
6719     appendChild : function(node){
6720         var multi = false;
6721         if(node instanceof Array){
6722             multi = node;
6723         }else if(arguments.length > 1){
6724             multi = arguments;
6725         }
6726         // if passed an array or multiple args do them one by one
6727         if(multi){
6728             for(var i = 0, len = multi.length; i < len; i++) {
6729                 this.appendChild(multi[i]);
6730             }
6731         }else{
6732             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6733                 return false;
6734             }
6735             var index = this.childNodes.length;
6736             var oldParent = node.parentNode;
6737             // it's a move, make sure we move it cleanly
6738             if(oldParent){
6739                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6740                     return false;
6741                 }
6742                 oldParent.removeChild(node);
6743             }
6744             index = this.childNodes.length;
6745             if(index == 0){
6746                 this.setFirstChild(node);
6747             }
6748             this.childNodes.push(node);
6749             node.parentNode = this;
6750             var ps = this.childNodes[index-1];
6751             if(ps){
6752                 node.previousSibling = ps;
6753                 ps.nextSibling = node;
6754             }else{
6755                 node.previousSibling = null;
6756             }
6757             node.nextSibling = null;
6758             this.setLastChild(node);
6759             node.setOwnerTree(this.getOwnerTree());
6760             this.fireEvent("append", this.ownerTree, this, node, index);
6761             if(oldParent){
6762                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6763             }
6764             return node;
6765         }
6766     },
6767
6768     /**
6769      * Removes a child node from this node.
6770      * @param {Node} node The node to remove
6771      * @return {Node} The removed node
6772      */
6773     removeChild : function(node){
6774         var index = this.childNodes.indexOf(node);
6775         if(index == -1){
6776             return false;
6777         }
6778         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6779             return false;
6780         }
6781
6782         // remove it from childNodes collection
6783         this.childNodes.splice(index, 1);
6784
6785         // update siblings
6786         if(node.previousSibling){
6787             node.previousSibling.nextSibling = node.nextSibling;
6788         }
6789         if(node.nextSibling){
6790             node.nextSibling.previousSibling = node.previousSibling;
6791         }
6792
6793         // update child refs
6794         if(this.firstChild == node){
6795             this.setFirstChild(node.nextSibling);
6796         }
6797         if(this.lastChild == node){
6798             this.setLastChild(node.previousSibling);
6799         }
6800
6801         node.setOwnerTree(null);
6802         // clear any references from the node
6803         node.parentNode = null;
6804         node.previousSibling = null;
6805         node.nextSibling = null;
6806         this.fireEvent("remove", this.ownerTree, this, node);
6807         return node;
6808     },
6809
6810     /**
6811      * Inserts the first node before the second node in this nodes childNodes collection.
6812      * @param {Node} node The node to insert
6813      * @param {Node} refNode The node to insert before (if null the node is appended)
6814      * @return {Node} The inserted node
6815      */
6816     insertBefore : function(node, refNode){
6817         if(!refNode){ // like standard Dom, refNode can be null for append
6818             return this.appendChild(node);
6819         }
6820         // nothing to do
6821         if(node == refNode){
6822             return false;
6823         }
6824
6825         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6826             return false;
6827         }
6828         var index = this.childNodes.indexOf(refNode);
6829         var oldParent = node.parentNode;
6830         var refIndex = index;
6831
6832         // when moving internally, indexes will change after remove
6833         if(oldParent == this && this.childNodes.indexOf(node) < index){
6834             refIndex--;
6835         }
6836
6837         // it's a move, make sure we move it cleanly
6838         if(oldParent){
6839             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6840                 return false;
6841             }
6842             oldParent.removeChild(node);
6843         }
6844         if(refIndex == 0){
6845             this.setFirstChild(node);
6846         }
6847         this.childNodes.splice(refIndex, 0, node);
6848         node.parentNode = this;
6849         var ps = this.childNodes[refIndex-1];
6850         if(ps){
6851             node.previousSibling = ps;
6852             ps.nextSibling = node;
6853         }else{
6854             node.previousSibling = null;
6855         }
6856         node.nextSibling = refNode;
6857         refNode.previousSibling = node;
6858         node.setOwnerTree(this.getOwnerTree());
6859         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6860         if(oldParent){
6861             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6862         }
6863         return node;
6864     },
6865
6866     /**
6867      * Returns the child node at the specified index.
6868      * @param {Number} index
6869      * @return {Node}
6870      */
6871     item : function(index){
6872         return this.childNodes[index];
6873     },
6874
6875     /**
6876      * Replaces one child node in this node with another.
6877      * @param {Node} newChild The replacement node
6878      * @param {Node} oldChild The node to replace
6879      * @return {Node} The replaced node
6880      */
6881     replaceChild : function(newChild, oldChild){
6882         this.insertBefore(newChild, oldChild);
6883         this.removeChild(oldChild);
6884         return oldChild;
6885     },
6886
6887     /**
6888      * Returns the index of a child node
6889      * @param {Node} node
6890      * @return {Number} The index of the node or -1 if it was not found
6891      */
6892     indexOf : function(child){
6893         return this.childNodes.indexOf(child);
6894     },
6895
6896     /**
6897      * Returns the tree this node is in.
6898      * @return {Tree}
6899      */
6900     getOwnerTree : function(){
6901         // if it doesn't have one, look for one
6902         if(!this.ownerTree){
6903             var p = this;
6904             while(p){
6905                 if(p.ownerTree){
6906                     this.ownerTree = p.ownerTree;
6907                     break;
6908                 }
6909                 p = p.parentNode;
6910             }
6911         }
6912         return this.ownerTree;
6913     },
6914
6915     /**
6916      * Returns depth of this node (the root node has a depth of 0)
6917      * @return {Number}
6918      */
6919     getDepth : function(){
6920         var depth = 0;
6921         var p = this;
6922         while(p.parentNode){
6923             ++depth;
6924             p = p.parentNode;
6925         }
6926         return depth;
6927     },
6928
6929     // private
6930     setOwnerTree : function(tree){
6931         // if it's move, we need to update everyone
6932         if(tree != this.ownerTree){
6933             if(this.ownerTree){
6934                 this.ownerTree.unregisterNode(this);
6935             }
6936             this.ownerTree = tree;
6937             var cs = this.childNodes;
6938             for(var i = 0, len = cs.length; i < len; i++) {
6939                 cs[i].setOwnerTree(tree);
6940             }
6941             if(tree){
6942                 tree.registerNode(this);
6943             }
6944         }
6945     },
6946
6947     /**
6948      * Returns the path for this node. The path can be used to expand or select this node programmatically.
6949      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
6950      * @return {String} The path
6951      */
6952     getPath : function(attr){
6953         attr = attr || "id";
6954         var p = this.parentNode;
6955         var b = [this.attributes[attr]];
6956         while(p){
6957             b.unshift(p.attributes[attr]);
6958             p = p.parentNode;
6959         }
6960         var sep = this.getOwnerTree().pathSeparator;
6961         return sep + b.join(sep);
6962     },
6963
6964     /**
6965      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
6966      * function call will be the scope provided or the current node. The arguments to the function
6967      * will be the args provided or the current node. If the function returns false at any point,
6968      * the bubble is stopped.
6969      * @param {Function} fn The function to call
6970      * @param {Object} scope (optional) The scope of the function (defaults to current node)
6971      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
6972      */
6973     bubble : function(fn, scope, args){
6974         var p = this;
6975         while(p){
6976             if(fn.call(scope || p, args || p) === false){
6977                 break;
6978             }
6979             p = p.parentNode;
6980         }
6981     },
6982
6983     /**
6984      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
6985      * function call will be the scope provided or the current node. The arguments to the function
6986      * will be the args provided or the current node. If the function returns false at any point,
6987      * the cascade is stopped on that branch.
6988      * @param {Function} fn The function to call
6989      * @param {Object} scope (optional) The scope of the function (defaults to current node)
6990      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
6991      */
6992     cascade : function(fn, scope, args){
6993         if(fn.call(scope || this, args || this) !== false){
6994             var cs = this.childNodes;
6995             for(var i = 0, len = cs.length; i < len; i++) {
6996                 cs[i].cascade(fn, scope, args);
6997             }
6998         }
6999     },
7000
7001     /**
7002      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7003      * function call will be the scope provided or the current node. The arguments to the function
7004      * will be the args provided or the current node. If the function returns false at any point,
7005      * the iteration stops.
7006      * @param {Function} fn The function to call
7007      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7008      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7009      */
7010     eachChild : function(fn, scope, args){
7011         var cs = this.childNodes;
7012         for(var i = 0, len = cs.length; i < len; i++) {
7013                 if(fn.call(scope || this, args || cs[i]) === false){
7014                     break;
7015                 }
7016         }
7017     },
7018
7019     /**
7020      * Finds the first child that has the attribute with the specified value.
7021      * @param {String} attribute The attribute name
7022      * @param {Mixed} value The value to search for
7023      * @return {Node} The found child or null if none was found
7024      */
7025     findChild : function(attribute, value){
7026         var cs = this.childNodes;
7027         for(var i = 0, len = cs.length; i < len; i++) {
7028                 if(cs[i].attributes[attribute] == value){
7029                     return cs[i];
7030                 }
7031         }
7032         return null;
7033     },
7034
7035     /**
7036      * Finds the first child by a custom function. The child matches if the function passed
7037      * returns true.
7038      * @param {Function} fn
7039      * @param {Object} scope (optional)
7040      * @return {Node} The found child or null if none was found
7041      */
7042     findChildBy : function(fn, scope){
7043         var cs = this.childNodes;
7044         for(var i = 0, len = cs.length; i < len; i++) {
7045                 if(fn.call(scope||cs[i], cs[i]) === true){
7046                     return cs[i];
7047                 }
7048         }
7049         return null;
7050     },
7051
7052     /**
7053      * Sorts this nodes children using the supplied sort function
7054      * @param {Function} fn
7055      * @param {Object} scope (optional)
7056      */
7057     sort : function(fn, scope){
7058         var cs = this.childNodes;
7059         var len = cs.length;
7060         if(len > 0){
7061             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7062             cs.sort(sortFn);
7063             for(var i = 0; i < len; i++){
7064                 var n = cs[i];
7065                 n.previousSibling = cs[i-1];
7066                 n.nextSibling = cs[i+1];
7067                 if(i == 0){
7068                     this.setFirstChild(n);
7069                 }
7070                 if(i == len-1){
7071                     this.setLastChild(n);
7072                 }
7073             }
7074         }
7075     },
7076
7077     /**
7078      * Returns true if this node is an ancestor (at any point) of the passed node.
7079      * @param {Node} node
7080      * @return {Boolean}
7081      */
7082     contains : function(node){
7083         return node.isAncestor(this);
7084     },
7085
7086     /**
7087      * Returns true if the passed node is an ancestor (at any point) of this node.
7088      * @param {Node} node
7089      * @return {Boolean}
7090      */
7091     isAncestor : function(node){
7092         var p = this.parentNode;
7093         while(p){
7094             if(p == node){
7095                 return true;
7096             }
7097             p = p.parentNode;
7098         }
7099         return false;
7100     },
7101
7102     toString : function(){
7103         return "[Node"+(this.id?" "+this.id:"")+"]";
7104     }
7105 });/*
7106  * Based on:
7107  * Ext JS Library 1.1.1
7108  * Copyright(c) 2006-2007, Ext JS, LLC.
7109  *
7110  * Originally Released Under LGPL - original licence link has changed is not relivant.
7111  *
7112  * Fork - LGPL
7113  * <script type="text/javascript">
7114  */
7115  
7116
7117 /**
7118  * @class Roo.ComponentMgr
7119  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7120  * @singleton
7121  */
7122 Roo.ComponentMgr = function(){
7123     var all = new Roo.util.MixedCollection();
7124
7125     return {
7126         /**
7127          * Registers a component.
7128          * @param {Roo.Component} c The component
7129          */
7130         register : function(c){
7131             all.add(c);
7132         },
7133
7134         /**
7135          * Unregisters a component.
7136          * @param {Roo.Component} c The component
7137          */
7138         unregister : function(c){
7139             all.remove(c);
7140         },
7141
7142         /**
7143          * Returns a component by id
7144          * @param {String} id The component id
7145          */
7146         get : function(id){
7147             return all.get(id);
7148         },
7149
7150         /**
7151          * Registers a function that will be called when a specified component is added to ComponentMgr
7152          * @param {String} id The component id
7153          * @param {Funtction} fn The callback function
7154          * @param {Object} scope The scope of the callback
7155          */
7156         onAvailable : function(id, fn, scope){
7157             all.on("add", function(index, o){
7158                 if(o.id == id){
7159                     fn.call(scope || o, o);
7160                     all.un("add", fn, scope);
7161                 }
7162             });
7163         }
7164     };
7165 }();/*
7166  * Based on:
7167  * Ext JS Library 1.1.1
7168  * Copyright(c) 2006-2007, Ext JS, LLC.
7169  *
7170  * Originally Released Under LGPL - original licence link has changed is not relivant.
7171  *
7172  * Fork - LGPL
7173  * <script type="text/javascript">
7174  */
7175  
7176 /**
7177  * @class Roo.Component
7178  * @extends Roo.util.Observable
7179  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7180  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7181  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7182  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7183  * All visual components (widgets) that require rendering into a layout should subclass Component.
7184  * @constructor
7185  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7186  * 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
7187  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7188  */
7189 Roo.Component = function(config){
7190     config = config || {};
7191     if(config.tagName || config.dom || typeof config == "string"){ // element object
7192         config = {el: config, id: config.id || config};
7193     }
7194     this.initialConfig = config;
7195
7196     Roo.apply(this, config);
7197     this.addEvents({
7198         /**
7199          * @event disable
7200          * Fires after the component is disabled.
7201              * @param {Roo.Component} this
7202              */
7203         disable : true,
7204         /**
7205          * @event enable
7206          * Fires after the component is enabled.
7207              * @param {Roo.Component} this
7208              */
7209         enable : true,
7210         /**
7211          * @event beforeshow
7212          * Fires before the component is shown.  Return false to stop the show.
7213              * @param {Roo.Component} this
7214              */
7215         beforeshow : true,
7216         /**
7217          * @event show
7218          * Fires after the component is shown.
7219              * @param {Roo.Component} this
7220              */
7221         show : true,
7222         /**
7223          * @event beforehide
7224          * Fires before the component is hidden. Return false to stop the hide.
7225              * @param {Roo.Component} this
7226              */
7227         beforehide : true,
7228         /**
7229          * @event hide
7230          * Fires after the component is hidden.
7231              * @param {Roo.Component} this
7232              */
7233         hide : true,
7234         /**
7235          * @event beforerender
7236          * Fires before the component is rendered. Return false to stop the render.
7237              * @param {Roo.Component} this
7238              */
7239         beforerender : true,
7240         /**
7241          * @event render
7242          * Fires after the component is rendered.
7243              * @param {Roo.Component} this
7244              */
7245         render : true,
7246         /**
7247          * @event beforedestroy
7248          * Fires before the component is destroyed. Return false to stop the destroy.
7249              * @param {Roo.Component} this
7250              */
7251         beforedestroy : true,
7252         /**
7253          * @event destroy
7254          * Fires after the component is destroyed.
7255              * @param {Roo.Component} this
7256              */
7257         destroy : true
7258     });
7259     if(!this.id){
7260         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7261     }
7262     Roo.ComponentMgr.register(this);
7263     Roo.Component.superclass.constructor.call(this);
7264     this.initComponent();
7265     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7266         this.render(this.renderTo);
7267         delete this.renderTo;
7268     }
7269 };
7270
7271 // private
7272 Roo.Component.AUTO_ID = 1000;
7273
7274 Roo.extend(Roo.Component, Roo.util.Observable, {
7275     /**
7276      * @property {Boolean} hidden
7277      * true if this component is hidden. Read-only.
7278      */
7279     hidden : false,
7280     /**
7281      * true if this component is disabled. Read-only.
7282      */
7283     disabled : false,
7284     /**
7285      * true if this component has been rendered. Read-only.
7286      */
7287     rendered : false,
7288     
7289     /** @cfg {String} disableClass
7290      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7291      */
7292     disabledClass : "x-item-disabled",
7293         /** @cfg {Boolean} allowDomMove
7294          * Whether the component can move the Dom node when rendering (defaults to true).
7295          */
7296     allowDomMove : true,
7297     /** @cfg {String} hideMode
7298      * How this component should hidden. Supported values are
7299      * "visibility" (css visibility), "offsets" (negative offset position) and
7300      * "display" (css display) - defaults to "display".
7301      */
7302     hideMode: 'display',
7303
7304     // private
7305     ctype : "Roo.Component",
7306
7307     /** @cfg {String} actionMode 
7308      * which property holds the element that used for  hide() / show() / disable() / enable()
7309      * default is 'el' 
7310      */
7311     actionMode : "el",
7312
7313     // private
7314     getActionEl : function(){
7315         return this[this.actionMode];
7316     },
7317
7318     initComponent : Roo.emptyFn,
7319     /**
7320      * If this is a lazy rendering component, render it to its container element.
7321      * @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.
7322      */
7323     render : function(container, position){
7324         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7325             if(!container && this.el){
7326                 this.el = Roo.get(this.el);
7327                 container = this.el.dom.parentNode;
7328                 this.allowDomMove = false;
7329             }
7330             this.container = Roo.get(container);
7331             this.rendered = true;
7332             if(position !== undefined){
7333                 if(typeof position == 'number'){
7334                     position = this.container.dom.childNodes[position];
7335                 }else{
7336                     position = Roo.getDom(position);
7337                 }
7338             }
7339             this.onRender(this.container, position || null);
7340             if(this.cls){
7341                 this.el.addClass(this.cls);
7342                 delete this.cls;
7343             }
7344             if(this.style){
7345                 this.el.applyStyles(this.style);
7346                 delete this.style;
7347             }
7348             this.fireEvent("render", this);
7349             this.afterRender(this.container);
7350             if(this.hidden){
7351                 this.hide();
7352             }
7353             if(this.disabled){
7354                 this.disable();
7355             }
7356         }
7357         return this;
7358     },
7359
7360     // private
7361     // default function is not really useful
7362     onRender : function(ct, position){
7363         if(this.el){
7364             this.el = Roo.get(this.el);
7365             if(this.allowDomMove !== false){
7366                 ct.dom.insertBefore(this.el.dom, position);
7367             }
7368         }
7369     },
7370
7371     // private
7372     getAutoCreate : function(){
7373         var cfg = typeof this.autoCreate == "object" ?
7374                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7375         if(this.id && !cfg.id){
7376             cfg.id = this.id;
7377         }
7378         return cfg;
7379     },
7380
7381     // private
7382     afterRender : Roo.emptyFn,
7383
7384     /**
7385      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7386      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7387      */
7388     destroy : function(){
7389         if(this.fireEvent("beforedestroy", this) !== false){
7390             this.purgeListeners();
7391             this.beforeDestroy();
7392             if(this.rendered){
7393                 this.el.removeAllListeners();
7394                 this.el.remove();
7395                 if(this.actionMode == "container"){
7396                     this.container.remove();
7397                 }
7398             }
7399             this.onDestroy();
7400             Roo.ComponentMgr.unregister(this);
7401             this.fireEvent("destroy", this);
7402         }
7403     },
7404
7405         // private
7406     beforeDestroy : function(){
7407
7408     },
7409
7410         // private
7411         onDestroy : function(){
7412
7413     },
7414
7415     /**
7416      * Returns the underlying {@link Roo.Element}.
7417      * @return {Roo.Element} The element
7418      */
7419     getEl : function(){
7420         return this.el;
7421     },
7422
7423     /**
7424      * Returns the id of this component.
7425      * @return {String}
7426      */
7427     getId : function(){
7428         return this.id;
7429     },
7430
7431     /**
7432      * Try to focus this component.
7433      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7434      * @return {Roo.Component} this
7435      */
7436     focus : function(selectText){
7437         if(this.rendered){
7438             this.el.focus();
7439             if(selectText === true){
7440                 this.el.dom.select();
7441             }
7442         }
7443         return this;
7444     },
7445
7446     // private
7447     blur : function(){
7448         if(this.rendered){
7449             this.el.blur();
7450         }
7451         return this;
7452     },
7453
7454     /**
7455      * Disable this component.
7456      * @return {Roo.Component} this
7457      */
7458     disable : function(){
7459         if(this.rendered){
7460             this.onDisable();
7461         }
7462         this.disabled = true;
7463         this.fireEvent("disable", this);
7464         return this;
7465     },
7466
7467         // private
7468     onDisable : function(){
7469         this.getActionEl().addClass(this.disabledClass);
7470         this.el.dom.disabled = true;
7471     },
7472
7473     /**
7474      * Enable this component.
7475      * @return {Roo.Component} this
7476      */
7477     enable : function(){
7478         if(this.rendered){
7479             this.onEnable();
7480         }
7481         this.disabled = false;
7482         this.fireEvent("enable", this);
7483         return this;
7484     },
7485
7486         // private
7487     onEnable : function(){
7488         this.getActionEl().removeClass(this.disabledClass);
7489         this.el.dom.disabled = false;
7490     },
7491
7492     /**
7493      * Convenience function for setting disabled/enabled by boolean.
7494      * @param {Boolean} disabled
7495      */
7496     setDisabled : function(disabled){
7497         this[disabled ? "disable" : "enable"]();
7498     },
7499
7500     /**
7501      * Show this component.
7502      * @return {Roo.Component} this
7503      */
7504     show: function(){
7505         if(this.fireEvent("beforeshow", this) !== false){
7506             this.hidden = false;
7507             if(this.rendered){
7508                 this.onShow();
7509             }
7510             this.fireEvent("show", this);
7511         }
7512         return this;
7513     },
7514
7515     // private
7516     onShow : function(){
7517         var ae = this.getActionEl();
7518         if(this.hideMode == 'visibility'){
7519             ae.dom.style.visibility = "visible";
7520         }else if(this.hideMode == 'offsets'){
7521             ae.removeClass('x-hidden');
7522         }else{
7523             ae.dom.style.display = "";
7524         }
7525     },
7526
7527     /**
7528      * Hide this component.
7529      * @return {Roo.Component} this
7530      */
7531     hide: function(){
7532         if(this.fireEvent("beforehide", this) !== false){
7533             this.hidden = true;
7534             if(this.rendered){
7535                 this.onHide();
7536             }
7537             this.fireEvent("hide", this);
7538         }
7539         return this;
7540     },
7541
7542     // private
7543     onHide : function(){
7544         var ae = this.getActionEl();
7545         if(this.hideMode == 'visibility'){
7546             ae.dom.style.visibility = "hidden";
7547         }else if(this.hideMode == 'offsets'){
7548             ae.addClass('x-hidden');
7549         }else{
7550             ae.dom.style.display = "none";
7551         }
7552     },
7553
7554     /**
7555      * Convenience function to hide or show this component by boolean.
7556      * @param {Boolean} visible True to show, false to hide
7557      * @return {Roo.Component} this
7558      */
7559     setVisible: function(visible){
7560         if(visible) {
7561             this.show();
7562         }else{
7563             this.hide();
7564         }
7565         return this;
7566     },
7567
7568     /**
7569      * Returns true if this component is visible.
7570      */
7571     isVisible : function(){
7572         return this.getActionEl().isVisible();
7573     },
7574
7575     cloneConfig : function(overrides){
7576         overrides = overrides || {};
7577         var id = overrides.id || Roo.id();
7578         var cfg = Roo.applyIf(overrides, this.initialConfig);
7579         cfg.id = id; // prevent dup id
7580         return new this.constructor(cfg);
7581     }
7582 });/*
7583  * Based on:
7584  * Ext JS Library 1.1.1
7585  * Copyright(c) 2006-2007, Ext JS, LLC.
7586  *
7587  * Originally Released Under LGPL - original licence link has changed is not relivant.
7588  *
7589  * Fork - LGPL
7590  * <script type="text/javascript">
7591  */
7592  (function(){ 
7593 /**
7594  * @class Roo.Layer
7595  * @extends Roo.Element
7596  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7597  * automatic maintaining of shadow/shim positions.
7598  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7599  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7600  * you can pass a string with a CSS class name. False turns off the shadow.
7601  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7602  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7603  * @cfg {String} cls CSS class to add to the element
7604  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7605  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7606  * @constructor
7607  * @param {Object} config An object with config options.
7608  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7609  */
7610
7611 Roo.Layer = function(config, existingEl){
7612     config = config || {};
7613     var dh = Roo.DomHelper;
7614     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7615     if(existingEl){
7616         this.dom = Roo.getDom(existingEl);
7617     }
7618     if(!this.dom){
7619         var o = config.dh || {tag: "div", cls: "x-layer"};
7620         this.dom = dh.append(pel, o);
7621     }
7622     if(config.cls){
7623         this.addClass(config.cls);
7624     }
7625     this.constrain = config.constrain !== false;
7626     this.visibilityMode = Roo.Element.VISIBILITY;
7627     if(config.id){
7628         this.id = this.dom.id = config.id;
7629     }else{
7630         this.id = Roo.id(this.dom);
7631     }
7632     this.zindex = config.zindex || this.getZIndex();
7633     this.position("absolute", this.zindex);
7634     if(config.shadow){
7635         this.shadowOffset = config.shadowOffset || 4;
7636         this.shadow = new Roo.Shadow({
7637             offset : this.shadowOffset,
7638             mode : config.shadow
7639         });
7640     }else{
7641         this.shadowOffset = 0;
7642     }
7643     this.useShim = config.shim !== false && Roo.useShims;
7644     this.useDisplay = config.useDisplay;
7645     this.hide();
7646 };
7647
7648 var supr = Roo.Element.prototype;
7649
7650 // shims are shared among layer to keep from having 100 iframes
7651 var shims = [];
7652
7653 Roo.extend(Roo.Layer, Roo.Element, {
7654
7655     getZIndex : function(){
7656         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7657     },
7658
7659     getShim : function(){
7660         if(!this.useShim){
7661             return null;
7662         }
7663         if(this.shim){
7664             return this.shim;
7665         }
7666         var shim = shims.shift();
7667         if(!shim){
7668             shim = this.createShim();
7669             shim.enableDisplayMode('block');
7670             shim.dom.style.display = 'none';
7671             shim.dom.style.visibility = 'visible';
7672         }
7673         var pn = this.dom.parentNode;
7674         if(shim.dom.parentNode != pn){
7675             pn.insertBefore(shim.dom, this.dom);
7676         }
7677         shim.setStyle('z-index', this.getZIndex()-2);
7678         this.shim = shim;
7679         return shim;
7680     },
7681
7682     hideShim : function(){
7683         if(this.shim){
7684             this.shim.setDisplayed(false);
7685             shims.push(this.shim);
7686             delete this.shim;
7687         }
7688     },
7689
7690     disableShadow : function(){
7691         if(this.shadow){
7692             this.shadowDisabled = true;
7693             this.shadow.hide();
7694             this.lastShadowOffset = this.shadowOffset;
7695             this.shadowOffset = 0;
7696         }
7697     },
7698
7699     enableShadow : function(show){
7700         if(this.shadow){
7701             this.shadowDisabled = false;
7702             this.shadowOffset = this.lastShadowOffset;
7703             delete this.lastShadowOffset;
7704             if(show){
7705                 this.sync(true);
7706             }
7707         }
7708     },
7709
7710     // private
7711     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7712     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7713     sync : function(doShow){
7714         var sw = this.shadow;
7715         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7716             var sh = this.getShim();
7717
7718             var w = this.getWidth(),
7719                 h = this.getHeight();
7720
7721             var l = this.getLeft(true),
7722                 t = this.getTop(true);
7723
7724             if(sw && !this.shadowDisabled){
7725                 if(doShow && !sw.isVisible()){
7726                     sw.show(this);
7727                 }else{
7728                     sw.realign(l, t, w, h);
7729                 }
7730                 if(sh){
7731                     if(doShow){
7732                        sh.show();
7733                     }
7734                     // fit the shim behind the shadow, so it is shimmed too
7735                     var a = sw.adjusts, s = sh.dom.style;
7736                     s.left = (Math.min(l, l+a.l))+"px";
7737                     s.top = (Math.min(t, t+a.t))+"px";
7738                     s.width = (w+a.w)+"px";
7739                     s.height = (h+a.h)+"px";
7740                 }
7741             }else if(sh){
7742                 if(doShow){
7743                    sh.show();
7744                 }
7745                 sh.setSize(w, h);
7746                 sh.setLeftTop(l, t);
7747             }
7748             
7749         }
7750     },
7751
7752     // private
7753     destroy : function(){
7754         this.hideShim();
7755         if(this.shadow){
7756             this.shadow.hide();
7757         }
7758         this.removeAllListeners();
7759         var pn = this.dom.parentNode;
7760         if(pn){
7761             pn.removeChild(this.dom);
7762         }
7763         Roo.Element.uncache(this.id);
7764     },
7765
7766     remove : function(){
7767         this.destroy();
7768     },
7769
7770     // private
7771     beginUpdate : function(){
7772         this.updating = true;
7773     },
7774
7775     // private
7776     endUpdate : function(){
7777         this.updating = false;
7778         this.sync(true);
7779     },
7780
7781     // private
7782     hideUnders : function(negOffset){
7783         if(this.shadow){
7784             this.shadow.hide();
7785         }
7786         this.hideShim();
7787     },
7788
7789     // private
7790     constrainXY : function(){
7791         if(this.constrain){
7792             var vw = Roo.lib.Dom.getViewWidth(),
7793                 vh = Roo.lib.Dom.getViewHeight();
7794             var s = Roo.get(document).getScroll();
7795
7796             var xy = this.getXY();
7797             var x = xy[0], y = xy[1];   
7798             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7799             // only move it if it needs it
7800             var moved = false;
7801             // first validate right/bottom
7802             if((x + w) > vw+s.left){
7803                 x = vw - w - this.shadowOffset;
7804                 moved = true;
7805             }
7806             if((y + h) > vh+s.top){
7807                 y = vh - h - this.shadowOffset;
7808                 moved = true;
7809             }
7810             // then make sure top/left isn't negative
7811             if(x < s.left){
7812                 x = s.left;
7813                 moved = true;
7814             }
7815             if(y < s.top){
7816                 y = s.top;
7817                 moved = true;
7818             }
7819             if(moved){
7820                 if(this.avoidY){
7821                     var ay = this.avoidY;
7822                     if(y <= ay && (y+h) >= ay){
7823                         y = ay-h-5;   
7824                     }
7825                 }
7826                 xy = [x, y];
7827                 this.storeXY(xy);
7828                 supr.setXY.call(this, xy);
7829                 this.sync();
7830             }
7831         }
7832     },
7833
7834     isVisible : function(){
7835         return this.visible;    
7836     },
7837
7838     // private
7839     showAction : function(){
7840         this.visible = true; // track visibility to prevent getStyle calls
7841         if(this.useDisplay === true){
7842             this.setDisplayed("");
7843         }else if(this.lastXY){
7844             supr.setXY.call(this, this.lastXY);
7845         }else if(this.lastLT){
7846             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7847         }
7848     },
7849
7850     // private
7851     hideAction : function(){
7852         this.visible = false;
7853         if(this.useDisplay === true){
7854             this.setDisplayed(false);
7855         }else{
7856             this.setLeftTop(-10000,-10000);
7857         }
7858     },
7859
7860     // overridden Element method
7861     setVisible : function(v, a, d, c, e){
7862         if(v){
7863             this.showAction();
7864         }
7865         if(a && v){
7866             var cb = function(){
7867                 this.sync(true);
7868                 if(c){
7869                     c();
7870                 }
7871             }.createDelegate(this);
7872             supr.setVisible.call(this, true, true, d, cb, e);
7873         }else{
7874             if(!v){
7875                 this.hideUnders(true);
7876             }
7877             var cb = c;
7878             if(a){
7879                 cb = function(){
7880                     this.hideAction();
7881                     if(c){
7882                         c();
7883                     }
7884                 }.createDelegate(this);
7885             }
7886             supr.setVisible.call(this, v, a, d, cb, e);
7887             if(v){
7888                 this.sync(true);
7889             }else if(!a){
7890                 this.hideAction();
7891             }
7892         }
7893     },
7894
7895     storeXY : function(xy){
7896         delete this.lastLT;
7897         this.lastXY = xy;
7898     },
7899
7900     storeLeftTop : function(left, top){
7901         delete this.lastXY;
7902         this.lastLT = [left, top];
7903     },
7904
7905     // private
7906     beforeFx : function(){
7907         this.beforeAction();
7908         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7909     },
7910
7911     // private
7912     afterFx : function(){
7913         Roo.Layer.superclass.afterFx.apply(this, arguments);
7914         this.sync(this.isVisible());
7915     },
7916
7917     // private
7918     beforeAction : function(){
7919         if(!this.updating && this.shadow){
7920             this.shadow.hide();
7921         }
7922     },
7923
7924     // overridden Element method
7925     setLeft : function(left){
7926         this.storeLeftTop(left, this.getTop(true));
7927         supr.setLeft.apply(this, arguments);
7928         this.sync();
7929     },
7930
7931     setTop : function(top){
7932         this.storeLeftTop(this.getLeft(true), top);
7933         supr.setTop.apply(this, arguments);
7934         this.sync();
7935     },
7936
7937     setLeftTop : function(left, top){
7938         this.storeLeftTop(left, top);
7939         supr.setLeftTop.apply(this, arguments);
7940         this.sync();
7941     },
7942
7943     setXY : function(xy, a, d, c, e){
7944         this.fixDisplay();
7945         this.beforeAction();
7946         this.storeXY(xy);
7947         var cb = this.createCB(c);
7948         supr.setXY.call(this, xy, a, d, cb, e);
7949         if(!a){
7950             cb();
7951         }
7952     },
7953
7954     // private
7955     createCB : function(c){
7956         var el = this;
7957         return function(){
7958             el.constrainXY();
7959             el.sync(true);
7960             if(c){
7961                 c();
7962             }
7963         };
7964     },
7965
7966     // overridden Element method
7967     setX : function(x, a, d, c, e){
7968         this.setXY([x, this.getY()], a, d, c, e);
7969     },
7970
7971     // overridden Element method
7972     setY : function(y, a, d, c, e){
7973         this.setXY([this.getX(), y], a, d, c, e);
7974     },
7975
7976     // overridden Element method
7977     setSize : function(w, h, a, d, c, e){
7978         this.beforeAction();
7979         var cb = this.createCB(c);
7980         supr.setSize.call(this, w, h, a, d, cb, e);
7981         if(!a){
7982             cb();
7983         }
7984     },
7985
7986     // overridden Element method
7987     setWidth : function(w, a, d, c, e){
7988         this.beforeAction();
7989         var cb = this.createCB(c);
7990         supr.setWidth.call(this, w, a, d, cb, e);
7991         if(!a){
7992             cb();
7993         }
7994     },
7995
7996     // overridden Element method
7997     setHeight : function(h, a, d, c, e){
7998         this.beforeAction();
7999         var cb = this.createCB(c);
8000         supr.setHeight.call(this, h, a, d, cb, e);
8001         if(!a){
8002             cb();
8003         }
8004     },
8005
8006     // overridden Element method
8007     setBounds : function(x, y, w, h, a, d, c, e){
8008         this.beforeAction();
8009         var cb = this.createCB(c);
8010         if(!a){
8011             this.storeXY([x, y]);
8012             supr.setXY.call(this, [x, y]);
8013             supr.setSize.call(this, w, h, a, d, cb, e);
8014             cb();
8015         }else{
8016             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8017         }
8018         return this;
8019     },
8020     
8021     /**
8022      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8023      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8024      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8025      * @param {Number} zindex The new z-index to set
8026      * @return {this} The Layer
8027      */
8028     setZIndex : function(zindex){
8029         this.zindex = zindex;
8030         this.setStyle("z-index", zindex + 2);
8031         if(this.shadow){
8032             this.shadow.setZIndex(zindex + 1);
8033         }
8034         if(this.shim){
8035             this.shim.setStyle("z-index", zindex);
8036         }
8037     }
8038 });
8039 })();/*
8040  * Based on:
8041  * Ext JS Library 1.1.1
8042  * Copyright(c) 2006-2007, Ext JS, LLC.
8043  *
8044  * Originally Released Under LGPL - original licence link has changed is not relivant.
8045  *
8046  * Fork - LGPL
8047  * <script type="text/javascript">
8048  */
8049
8050
8051 /**
8052  * @class Roo.Shadow
8053  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8054  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8055  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8056  * @constructor
8057  * Create a new Shadow
8058  * @param {Object} config The config object
8059  */
8060 Roo.Shadow = function(config){
8061     Roo.apply(this, config);
8062     if(typeof this.mode != "string"){
8063         this.mode = this.defaultMode;
8064     }
8065     var o = this.offset, a = {h: 0};
8066     var rad = Math.floor(this.offset/2);
8067     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8068         case "drop":
8069             a.w = 0;
8070             a.l = a.t = o;
8071             a.t -= 1;
8072             if(Roo.isIE){
8073                 a.l -= this.offset + rad;
8074                 a.t -= this.offset + rad;
8075                 a.w -= rad;
8076                 a.h -= rad;
8077                 a.t += 1;
8078             }
8079         break;
8080         case "sides":
8081             a.w = (o*2);
8082             a.l = -o;
8083             a.t = o-1;
8084             if(Roo.isIE){
8085                 a.l -= (this.offset - rad);
8086                 a.t -= this.offset + rad;
8087                 a.l += 1;
8088                 a.w -= (this.offset - rad)*2;
8089                 a.w -= rad + 1;
8090                 a.h -= 1;
8091             }
8092         break;
8093         case "frame":
8094             a.w = a.h = (o*2);
8095             a.l = a.t = -o;
8096             a.t += 1;
8097             a.h -= 2;
8098             if(Roo.isIE){
8099                 a.l -= (this.offset - rad);
8100                 a.t -= (this.offset - rad);
8101                 a.l += 1;
8102                 a.w -= (this.offset + rad + 1);
8103                 a.h -= (this.offset + rad);
8104                 a.h += 1;
8105             }
8106         break;
8107     };
8108
8109     this.adjusts = a;
8110 };
8111
8112 Roo.Shadow.prototype = {
8113     /**
8114      * @cfg {String} mode
8115      * The shadow display mode.  Supports the following options:<br />
8116      * sides: Shadow displays on both sides and bottom only<br />
8117      * frame: Shadow displays equally on all four sides<br />
8118      * drop: Traditional bottom-right drop shadow (default)
8119      */
8120     /**
8121      * @cfg {String} offset
8122      * The number of pixels to offset the shadow from the element (defaults to 4)
8123      */
8124     offset: 4,
8125
8126     // private
8127     defaultMode: "drop",
8128
8129     /**
8130      * Displays the shadow under the target element
8131      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8132      */
8133     show : function(target){
8134         target = Roo.get(target);
8135         if(!this.el){
8136             this.el = Roo.Shadow.Pool.pull();
8137             if(this.el.dom.nextSibling != target.dom){
8138                 this.el.insertBefore(target);
8139             }
8140         }
8141         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8142         if(Roo.isIE){
8143             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8144         }
8145         this.realign(
8146             target.getLeft(true),
8147             target.getTop(true),
8148             target.getWidth(),
8149             target.getHeight()
8150         );
8151         this.el.dom.style.display = "block";
8152     },
8153
8154     /**
8155      * Returns true if the shadow is visible, else false
8156      */
8157     isVisible : function(){
8158         return this.el ? true : false;  
8159     },
8160
8161     /**
8162      * Direct alignment when values are already available. Show must be called at least once before
8163      * calling this method to ensure it is initialized.
8164      * @param {Number} left The target element left position
8165      * @param {Number} top The target element top position
8166      * @param {Number} width The target element width
8167      * @param {Number} height The target element height
8168      */
8169     realign : function(l, t, w, h){
8170         if(!this.el){
8171             return;
8172         }
8173         var a = this.adjusts, d = this.el.dom, s = d.style;
8174         var iea = 0;
8175         s.left = (l+a.l)+"px";
8176         s.top = (t+a.t)+"px";
8177         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8178         if(s.width != sws || s.height != shs){
8179             s.width = sws;
8180             s.height = shs;
8181             if(!Roo.isIE){
8182                 var cn = d.childNodes;
8183                 var sww = Math.max(0, (sw-12))+"px";
8184                 cn[0].childNodes[1].style.width = sww;
8185                 cn[1].childNodes[1].style.width = sww;
8186                 cn[2].childNodes[1].style.width = sww;
8187                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8188             }
8189         }
8190     },
8191
8192     /**
8193      * Hides this shadow
8194      */
8195     hide : function(){
8196         if(this.el){
8197             this.el.dom.style.display = "none";
8198             Roo.Shadow.Pool.push(this.el);
8199             delete this.el;
8200         }
8201     },
8202
8203     /**
8204      * Adjust the z-index of this shadow
8205      * @param {Number} zindex The new z-index
8206      */
8207     setZIndex : function(z){
8208         this.zIndex = z;
8209         if(this.el){
8210             this.el.setStyle("z-index", z);
8211         }
8212     }
8213 };
8214
8215 // Private utility class that manages the internal Shadow cache
8216 Roo.Shadow.Pool = function(){
8217     var p = [];
8218     var markup = Roo.isIE ?
8219                  '<div class="x-ie-shadow"></div>' :
8220                  '<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>';
8221     return {
8222         pull : function(){
8223             var sh = p.shift();
8224             if(!sh){
8225                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8226                 sh.autoBoxAdjust = false;
8227             }
8228             return sh;
8229         },
8230
8231         push : function(sh){
8232             p.push(sh);
8233         }
8234     };
8235 }();/*
8236  * Based on:
8237  * Ext JS Library 1.1.1
8238  * Copyright(c) 2006-2007, Ext JS, LLC.
8239  *
8240  * Originally Released Under LGPL - original licence link has changed is not relivant.
8241  *
8242  * Fork - LGPL
8243  * <script type="text/javascript">
8244  */
8245
8246 /**
8247  * @class Roo.BoxComponent
8248  * @extends Roo.Component
8249  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8250  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8251  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8252  * layout containers.
8253  * @constructor
8254  * @param {Roo.Element/String/Object} config The configuration options.
8255  */
8256 Roo.BoxComponent = function(config){
8257     Roo.Component.call(this, config);
8258     this.addEvents({
8259         /**
8260          * @event resize
8261          * Fires after the component is resized.
8262              * @param {Roo.Component} this
8263              * @param {Number} adjWidth The box-adjusted width that was set
8264              * @param {Number} adjHeight The box-adjusted height that was set
8265              * @param {Number} rawWidth The width that was originally specified
8266              * @param {Number} rawHeight The height that was originally specified
8267              */
8268         resize : true,
8269         /**
8270          * @event move
8271          * Fires after the component is moved.
8272              * @param {Roo.Component} this
8273              * @param {Number} x The new x position
8274              * @param {Number} y The new y position
8275              */
8276         move : true
8277     });
8278 };
8279
8280 Roo.extend(Roo.BoxComponent, Roo.Component, {
8281     // private, set in afterRender to signify that the component has been rendered
8282     boxReady : false,
8283     // private, used to defer height settings to subclasses
8284     deferHeight: false,
8285     /** @cfg {Number} width
8286      * width (optional) size of component
8287      */
8288      /** @cfg {Number} height
8289      * height (optional) size of component
8290      */
8291      
8292     /**
8293      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8294      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8295      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8296      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8297      * @return {Roo.BoxComponent} this
8298      */
8299     setSize : function(w, h){
8300         // support for standard size objects
8301         if(typeof w == 'object'){
8302             h = w.height;
8303             w = w.width;
8304         }
8305         // not rendered
8306         if(!this.boxReady){
8307             this.width = w;
8308             this.height = h;
8309             return this;
8310         }
8311
8312         // prevent recalcs when not needed
8313         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8314             return this;
8315         }
8316         this.lastSize = {width: w, height: h};
8317
8318         var adj = this.adjustSize(w, h);
8319         var aw = adj.width, ah = adj.height;
8320         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8321             var rz = this.getResizeEl();
8322             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8323                 rz.setSize(aw, ah);
8324             }else if(!this.deferHeight && ah !== undefined){
8325                 rz.setHeight(ah);
8326             }else if(aw !== undefined){
8327                 rz.setWidth(aw);
8328             }
8329             this.onResize(aw, ah, w, h);
8330             this.fireEvent('resize', this, aw, ah, w, h);
8331         }
8332         return this;
8333     },
8334
8335     /**
8336      * Gets the current size of the component's underlying element.
8337      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8338      */
8339     getSize : function(){
8340         return this.el.getSize();
8341     },
8342
8343     /**
8344      * Gets the current XY position of the component's underlying element.
8345      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8346      * @return {Array} The XY position of the element (e.g., [100, 200])
8347      */
8348     getPosition : function(local){
8349         if(local === true){
8350             return [this.el.getLeft(true), this.el.getTop(true)];
8351         }
8352         return this.xy || this.el.getXY();
8353     },
8354
8355     /**
8356      * Gets the current box measurements of the component's underlying element.
8357      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8358      * @returns {Object} box An object in the format {x, y, width, height}
8359      */
8360     getBox : function(local){
8361         var s = this.el.getSize();
8362         if(local){
8363             s.x = this.el.getLeft(true);
8364             s.y = this.el.getTop(true);
8365         }else{
8366             var xy = this.xy || this.el.getXY();
8367             s.x = xy[0];
8368             s.y = xy[1];
8369         }
8370         return s;
8371     },
8372
8373     /**
8374      * Sets the current box measurements of the component's underlying element.
8375      * @param {Object} box An object in the format {x, y, width, height}
8376      * @returns {Roo.BoxComponent} this
8377      */
8378     updateBox : function(box){
8379         this.setSize(box.width, box.height);
8380         this.setPagePosition(box.x, box.y);
8381         return this;
8382     },
8383
8384     // protected
8385     getResizeEl : function(){
8386         return this.resizeEl || this.el;
8387     },
8388
8389     // protected
8390     getPositionEl : function(){
8391         return this.positionEl || this.el;
8392     },
8393
8394     /**
8395      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8396      * This method fires the move event.
8397      * @param {Number} left The new left
8398      * @param {Number} top The new top
8399      * @returns {Roo.BoxComponent} this
8400      */
8401     setPosition : function(x, y){
8402         this.x = x;
8403         this.y = y;
8404         if(!this.boxReady){
8405             return this;
8406         }
8407         var adj = this.adjustPosition(x, y);
8408         var ax = adj.x, ay = adj.y;
8409
8410         var el = this.getPositionEl();
8411         if(ax !== undefined || ay !== undefined){
8412             if(ax !== undefined && ay !== undefined){
8413                 el.setLeftTop(ax, ay);
8414             }else if(ax !== undefined){
8415                 el.setLeft(ax);
8416             }else if(ay !== undefined){
8417                 el.setTop(ay);
8418             }
8419             this.onPosition(ax, ay);
8420             this.fireEvent('move', this, ax, ay);
8421         }
8422         return this;
8423     },
8424
8425     /**
8426      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8427      * This method fires the move event.
8428      * @param {Number} x The new x position
8429      * @param {Number} y The new y position
8430      * @returns {Roo.BoxComponent} this
8431      */
8432     setPagePosition : function(x, y){
8433         this.pageX = x;
8434         this.pageY = y;
8435         if(!this.boxReady){
8436             return;
8437         }
8438         if(x === undefined || y === undefined){ // cannot translate undefined points
8439             return;
8440         }
8441         var p = this.el.translatePoints(x, y);
8442         this.setPosition(p.left, p.top);
8443         return this;
8444     },
8445
8446     // private
8447     onRender : function(ct, position){
8448         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8449         if(this.resizeEl){
8450             this.resizeEl = Roo.get(this.resizeEl);
8451         }
8452         if(this.positionEl){
8453             this.positionEl = Roo.get(this.positionEl);
8454         }
8455     },
8456
8457     // private
8458     afterRender : function(){
8459         Roo.BoxComponent.superclass.afterRender.call(this);
8460         this.boxReady = true;
8461         this.setSize(this.width, this.height);
8462         if(this.x || this.y){
8463             this.setPosition(this.x, this.y);
8464         }
8465         if(this.pageX || this.pageY){
8466             this.setPagePosition(this.pageX, this.pageY);
8467         }
8468     },
8469
8470     /**
8471      * Force the component's size to recalculate based on the underlying element's current height and width.
8472      * @returns {Roo.BoxComponent} this
8473      */
8474     syncSize : function(){
8475         delete this.lastSize;
8476         this.setSize(this.el.getWidth(), this.el.getHeight());
8477         return this;
8478     },
8479
8480     /**
8481      * Called after the component is resized, this method is empty by default but can be implemented by any
8482      * subclass that needs to perform custom logic after a resize occurs.
8483      * @param {Number} adjWidth The box-adjusted width that was set
8484      * @param {Number} adjHeight The box-adjusted height that was set
8485      * @param {Number} rawWidth The width that was originally specified
8486      * @param {Number} rawHeight The height that was originally specified
8487      */
8488     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8489
8490     },
8491
8492     /**
8493      * Called after the component is moved, this method is empty by default but can be implemented by any
8494      * subclass that needs to perform custom logic after a move occurs.
8495      * @param {Number} x The new x position
8496      * @param {Number} y The new y position
8497      */
8498     onPosition : function(x, y){
8499
8500     },
8501
8502     // private
8503     adjustSize : function(w, h){
8504         if(this.autoWidth){
8505             w = 'auto';
8506         }
8507         if(this.autoHeight){
8508             h = 'auto';
8509         }
8510         return {width : w, height: h};
8511     },
8512
8513     // private
8514     adjustPosition : function(x, y){
8515         return {x : x, y: y};
8516     }
8517 });/*
8518  * Based on:
8519  * Ext JS Library 1.1.1
8520  * Copyright(c) 2006-2007, Ext JS, LLC.
8521  *
8522  * Originally Released Under LGPL - original licence link has changed is not relivant.
8523  *
8524  * Fork - LGPL
8525  * <script type="text/javascript">
8526  */
8527
8528
8529 /**
8530  * @class Roo.SplitBar
8531  * @extends Roo.util.Observable
8532  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8533  * <br><br>
8534  * Usage:
8535  * <pre><code>
8536 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8537                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8538 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8539 split.minSize = 100;
8540 split.maxSize = 600;
8541 split.animate = true;
8542 split.on('moved', splitterMoved);
8543 </code></pre>
8544  * @constructor
8545  * Create a new SplitBar
8546  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8547  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8548  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8549  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8550                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8551                         position of the SplitBar).
8552  */
8553 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8554     
8555     /** @private */
8556     this.el = Roo.get(dragElement, true);
8557     this.el.dom.unselectable = "on";
8558     /** @private */
8559     this.resizingEl = Roo.get(resizingElement, true);
8560
8561     /**
8562      * @private
8563      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8564      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8565      * @type Number
8566      */
8567     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8568     
8569     /**
8570      * The minimum size of the resizing element. (Defaults to 0)
8571      * @type Number
8572      */
8573     this.minSize = 0;
8574     
8575     /**
8576      * The maximum size of the resizing element. (Defaults to 2000)
8577      * @type Number
8578      */
8579     this.maxSize = 2000;
8580     
8581     /**
8582      * Whether to animate the transition to the new size
8583      * @type Boolean
8584      */
8585     this.animate = false;
8586     
8587     /**
8588      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8589      * @type Boolean
8590      */
8591     this.useShim = false;
8592     
8593     /** @private */
8594     this.shim = null;
8595     
8596     if(!existingProxy){
8597         /** @private */
8598         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8599     }else{
8600         this.proxy = Roo.get(existingProxy).dom;
8601     }
8602     /** @private */
8603     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8604     
8605     /** @private */
8606     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8607     
8608     /** @private */
8609     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8610     
8611     /** @private */
8612     this.dragSpecs = {};
8613     
8614     /**
8615      * @private The adapter to use to positon and resize elements
8616      */
8617     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8618     this.adapter.init(this);
8619     
8620     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8621         /** @private */
8622         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8623         this.el.addClass("x-splitbar-h");
8624     }else{
8625         /** @private */
8626         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8627         this.el.addClass("x-splitbar-v");
8628     }
8629     
8630     this.addEvents({
8631         /**
8632          * @event resize
8633          * Fires when the splitter is moved (alias for {@link #event-moved})
8634          * @param {Roo.SplitBar} this
8635          * @param {Number} newSize the new width or height
8636          */
8637         "resize" : true,
8638         /**
8639          * @event moved
8640          * Fires when the splitter is moved
8641          * @param {Roo.SplitBar} this
8642          * @param {Number} newSize the new width or height
8643          */
8644         "moved" : true,
8645         /**
8646          * @event beforeresize
8647          * Fires before the splitter is dragged
8648          * @param {Roo.SplitBar} this
8649          */
8650         "beforeresize" : true,
8651
8652         "beforeapply" : true
8653     });
8654
8655     Roo.util.Observable.call(this);
8656 };
8657
8658 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8659     onStartProxyDrag : function(x, y){
8660         this.fireEvent("beforeresize", this);
8661         if(!this.overlay){
8662             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8663             o.unselectable();
8664             o.enableDisplayMode("block");
8665             // all splitbars share the same overlay
8666             Roo.SplitBar.prototype.overlay = o;
8667         }
8668         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8669         this.overlay.show();
8670         Roo.get(this.proxy).setDisplayed("block");
8671         var size = this.adapter.getElementSize(this);
8672         this.activeMinSize = this.getMinimumSize();;
8673         this.activeMaxSize = this.getMaximumSize();;
8674         var c1 = size - this.activeMinSize;
8675         var c2 = Math.max(this.activeMaxSize - size, 0);
8676         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8677             this.dd.resetConstraints();
8678             this.dd.setXConstraint(
8679                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8680                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8681             );
8682             this.dd.setYConstraint(0, 0);
8683         }else{
8684             this.dd.resetConstraints();
8685             this.dd.setXConstraint(0, 0);
8686             this.dd.setYConstraint(
8687                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8688                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8689             );
8690          }
8691         this.dragSpecs.startSize = size;
8692         this.dragSpecs.startPoint = [x, y];
8693         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8694     },
8695     
8696     /** 
8697      * @private Called after the drag operation by the DDProxy
8698      */
8699     onEndProxyDrag : function(e){
8700         Roo.get(this.proxy).setDisplayed(false);
8701         var endPoint = Roo.lib.Event.getXY(e);
8702         if(this.overlay){
8703             this.overlay.hide();
8704         }
8705         var newSize;
8706         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8707             newSize = this.dragSpecs.startSize + 
8708                 (this.placement == Roo.SplitBar.LEFT ?
8709                     endPoint[0] - this.dragSpecs.startPoint[0] :
8710                     this.dragSpecs.startPoint[0] - endPoint[0]
8711                 );
8712         }else{
8713             newSize = this.dragSpecs.startSize + 
8714                 (this.placement == Roo.SplitBar.TOP ?
8715                     endPoint[1] - this.dragSpecs.startPoint[1] :
8716                     this.dragSpecs.startPoint[1] - endPoint[1]
8717                 );
8718         }
8719         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8720         if(newSize != this.dragSpecs.startSize){
8721             if(this.fireEvent('beforeapply', this, newSize) !== false){
8722                 this.adapter.setElementSize(this, newSize);
8723                 this.fireEvent("moved", this, newSize);
8724                 this.fireEvent("resize", this, newSize);
8725             }
8726         }
8727     },
8728     
8729     /**
8730      * Get the adapter this SplitBar uses
8731      * @return The adapter object
8732      */
8733     getAdapter : function(){
8734         return this.adapter;
8735     },
8736     
8737     /**
8738      * Set the adapter this SplitBar uses
8739      * @param {Object} adapter A SplitBar adapter object
8740      */
8741     setAdapter : function(adapter){
8742         this.adapter = adapter;
8743         this.adapter.init(this);
8744     },
8745     
8746     /**
8747      * Gets the minimum size for the resizing element
8748      * @return {Number} The minimum size
8749      */
8750     getMinimumSize : function(){
8751         return this.minSize;
8752     },
8753     
8754     /**
8755      * Sets the minimum size for the resizing element
8756      * @param {Number} minSize The minimum size
8757      */
8758     setMinimumSize : function(minSize){
8759         this.minSize = minSize;
8760     },
8761     
8762     /**
8763      * Gets the maximum size for the resizing element
8764      * @return {Number} The maximum size
8765      */
8766     getMaximumSize : function(){
8767         return this.maxSize;
8768     },
8769     
8770     /**
8771      * Sets the maximum size for the resizing element
8772      * @param {Number} maxSize The maximum size
8773      */
8774     setMaximumSize : function(maxSize){
8775         this.maxSize = maxSize;
8776     },
8777     
8778     /**
8779      * Sets the initialize size for the resizing element
8780      * @param {Number} size The initial size
8781      */
8782     setCurrentSize : function(size){
8783         var oldAnimate = this.animate;
8784         this.animate = false;
8785         this.adapter.setElementSize(this, size);
8786         this.animate = oldAnimate;
8787     },
8788     
8789     /**
8790      * Destroy this splitbar. 
8791      * @param {Boolean} removeEl True to remove the element
8792      */
8793     destroy : function(removeEl){
8794         if(this.shim){
8795             this.shim.remove();
8796         }
8797         this.dd.unreg();
8798         this.proxy.parentNode.removeChild(this.proxy);
8799         if(removeEl){
8800             this.el.remove();
8801         }
8802     }
8803 });
8804
8805 /**
8806  * @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.
8807  */
8808 Roo.SplitBar.createProxy = function(dir){
8809     var proxy = new Roo.Element(document.createElement("div"));
8810     proxy.unselectable();
8811     var cls = 'x-splitbar-proxy';
8812     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8813     document.body.appendChild(proxy.dom);
8814     return proxy.dom;
8815 };
8816
8817 /** 
8818  * @class Roo.SplitBar.BasicLayoutAdapter
8819  * Default Adapter. It assumes the splitter and resizing element are not positioned
8820  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8821  */
8822 Roo.SplitBar.BasicLayoutAdapter = function(){
8823 };
8824
8825 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8826     // do nothing for now
8827     init : function(s){
8828     
8829     },
8830     /**
8831      * Called before drag operations to get the current size of the resizing element. 
8832      * @param {Roo.SplitBar} s The SplitBar using this adapter
8833      */
8834      getElementSize : function(s){
8835         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8836             return s.resizingEl.getWidth();
8837         }else{
8838             return s.resizingEl.getHeight();
8839         }
8840     },
8841     
8842     /**
8843      * Called after drag operations to set the size of the resizing element.
8844      * @param {Roo.SplitBar} s The SplitBar using this adapter
8845      * @param {Number} newSize The new size to set
8846      * @param {Function} onComplete A function to be invoked when resizing is complete
8847      */
8848     setElementSize : function(s, newSize, onComplete){
8849         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8850             if(!s.animate){
8851                 s.resizingEl.setWidth(newSize);
8852                 if(onComplete){
8853                     onComplete(s, newSize);
8854                 }
8855             }else{
8856                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8857             }
8858         }else{
8859             
8860             if(!s.animate){
8861                 s.resizingEl.setHeight(newSize);
8862                 if(onComplete){
8863                     onComplete(s, newSize);
8864                 }
8865             }else{
8866                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8867             }
8868         }
8869     }
8870 };
8871
8872 /** 
8873  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8874  * @extends Roo.SplitBar.BasicLayoutAdapter
8875  * Adapter that  moves the splitter element to align with the resized sizing element. 
8876  * Used with an absolute positioned SplitBar.
8877  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8878  * document.body, make sure you assign an id to the body element.
8879  */
8880 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8881     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8882     this.container = Roo.get(container);
8883 };
8884
8885 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8886     init : function(s){
8887         this.basic.init(s);
8888     },
8889     
8890     getElementSize : function(s){
8891         return this.basic.getElementSize(s);
8892     },
8893     
8894     setElementSize : function(s, newSize, onComplete){
8895         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8896     },
8897     
8898     moveSplitter : function(s){
8899         var yes = Roo.SplitBar;
8900         switch(s.placement){
8901             case yes.LEFT:
8902                 s.el.setX(s.resizingEl.getRight());
8903                 break;
8904             case yes.RIGHT:
8905                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8906                 break;
8907             case yes.TOP:
8908                 s.el.setY(s.resizingEl.getBottom());
8909                 break;
8910             case yes.BOTTOM:
8911                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8912                 break;
8913         }
8914     }
8915 };
8916
8917 /**
8918  * Orientation constant - Create a vertical SplitBar
8919  * @static
8920  * @type Number
8921  */
8922 Roo.SplitBar.VERTICAL = 1;
8923
8924 /**
8925  * Orientation constant - Create a horizontal SplitBar
8926  * @static
8927  * @type Number
8928  */
8929 Roo.SplitBar.HORIZONTAL = 2;
8930
8931 /**
8932  * Placement constant - The resizing element is to the left of the splitter element
8933  * @static
8934  * @type Number
8935  */
8936 Roo.SplitBar.LEFT = 1;
8937
8938 /**
8939  * Placement constant - The resizing element is to the right of the splitter element
8940  * @static
8941  * @type Number
8942  */
8943 Roo.SplitBar.RIGHT = 2;
8944
8945 /**
8946  * Placement constant - The resizing element is positioned above the splitter element
8947  * @static
8948  * @type Number
8949  */
8950 Roo.SplitBar.TOP = 3;
8951
8952 /**
8953  * Placement constant - The resizing element is positioned under splitter element
8954  * @static
8955  * @type Number
8956  */
8957 Roo.SplitBar.BOTTOM = 4;
8958 /*
8959  * Based on:
8960  * Ext JS Library 1.1.1
8961  * Copyright(c) 2006-2007, Ext JS, LLC.
8962  *
8963  * Originally Released Under LGPL - original licence link has changed is not relivant.
8964  *
8965  * Fork - LGPL
8966  * <script type="text/javascript">
8967  */
8968
8969 /**
8970  * @class Roo.View
8971  * @extends Roo.util.Observable
8972  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8973  * This class also supports single and multi selection modes. <br>
8974  * Create a data model bound view:
8975  <pre><code>
8976  var store = new Roo.data.Store(...);
8977
8978  var view = new Roo.View({
8979     el : "my-element",
8980     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8981  
8982     singleSelect: true,
8983     selectedClass: "ydataview-selected",
8984     store: store
8985  });
8986
8987  // listen for node click?
8988  view.on("click", function(vw, index, node, e){
8989  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8990  });
8991
8992  // load XML data
8993  dataModel.load("foobar.xml");
8994  </code></pre>
8995  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8996  * <br><br>
8997  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8998  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8999  * 
9000  * Note: old style constructor is still suported (container, template, config)
9001  * 
9002  * @constructor
9003  * Create a new View
9004  * @param {Object} config The config object
9005  * 
9006  */
9007 Roo.View = function(config, depreciated_tpl, depreciated_config){
9008     
9009     if (typeof(depreciated_tpl) == 'undefined') {
9010         // new way.. - universal constructor.
9011         Roo.apply(this, config);
9012         this.el  = Roo.get(this.el);
9013     } else {
9014         // old format..
9015         this.el  = Roo.get(config);
9016         this.tpl = depreciated_tpl;
9017         Roo.apply(this, depreciated_config);
9018     }
9019      
9020     
9021     if(typeof(this.tpl) == "string"){
9022         this.tpl = new Roo.Template(this.tpl);
9023     } else {
9024         // support xtype ctors..
9025         this.tpl = new Roo.factory(this.tpl, Roo);
9026     }
9027     
9028     
9029     this.tpl.compile();
9030    
9031
9032      
9033     /** @private */
9034     this.addEvents({
9035     /**
9036      * @event beforeclick
9037      * Fires before a click is processed. Returns false to cancel the default action.
9038      * @param {Roo.View} this
9039      * @param {Number} index The index of the target node
9040      * @param {HTMLElement} node The target node
9041      * @param {Roo.EventObject} e The raw event object
9042      */
9043         "beforeclick" : true,
9044     /**
9045      * @event click
9046      * Fires when a template node is clicked.
9047      * @param {Roo.View} this
9048      * @param {Number} index The index of the target node
9049      * @param {HTMLElement} node The target node
9050      * @param {Roo.EventObject} e The raw event object
9051      */
9052         "click" : true,
9053     /**
9054      * @event dblclick
9055      * Fires when a template node is double clicked.
9056      * @param {Roo.View} this
9057      * @param {Number} index The index of the target node
9058      * @param {HTMLElement} node The target node
9059      * @param {Roo.EventObject} e The raw event object
9060      */
9061         "dblclick" : true,
9062     /**
9063      * @event contextmenu
9064      * Fires when a template node is right clicked.
9065      * @param {Roo.View} this
9066      * @param {Number} index The index of the target node
9067      * @param {HTMLElement} node The target node
9068      * @param {Roo.EventObject} e The raw event object
9069      */
9070         "contextmenu" : true,
9071     /**
9072      * @event selectionchange
9073      * Fires when the selected nodes change.
9074      * @param {Roo.View} this
9075      * @param {Array} selections Array of the selected nodes
9076      */
9077         "selectionchange" : true,
9078
9079     /**
9080      * @event beforeselect
9081      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9082      * @param {Roo.View} this
9083      * @param {HTMLElement} node The node to be selected
9084      * @param {Array} selections Array of currently selected nodes
9085      */
9086         "beforeselect" : true
9087     });
9088
9089     this.el.on({
9090         "click": this.onClick,
9091         "dblclick": this.onDblClick,
9092         "contextmenu": this.onContextMenu,
9093         scope:this
9094     });
9095
9096     this.selections = [];
9097     this.nodes = [];
9098     this.cmp = new Roo.CompositeElementLite([]);
9099     if(this.store){
9100         this.store = Roo.factory(this.store, Roo.data);
9101         this.setStore(this.store, true);
9102     }
9103     Roo.View.superclass.constructor.call(this);
9104 };
9105
9106 Roo.extend(Roo.View, Roo.util.Observable, {
9107     
9108      /**
9109      * @cfg {Roo.data.Store} store Data store to load data from.
9110      */
9111     store : false,
9112     
9113     /**
9114      * @cfg {String|Roo.Element} el The container element.
9115      */
9116     el : '',
9117     
9118     /**
9119      * @cfg {String|Roo.Template} tpl The template used by this View 
9120      */
9121     tpl : false,
9122     
9123     /**
9124      * @cfg {String} selectedClass The css class to add to selected nodes
9125      */
9126     selectedClass : "x-view-selected",
9127      /**
9128      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9129      */
9130     emptyText : "",
9131     /**
9132      * @cfg {Boolean} multiSelect Allow multiple selection
9133      */
9134     
9135     multiSelect : false,
9136     /**
9137      * @cfg {Boolean} singleSelect Allow single selection
9138      */
9139     singleSelect:  false,
9140     
9141     /**
9142      * Returns the element this view is bound to.
9143      * @return {Roo.Element}
9144      */
9145     getEl : function(){
9146         return this.el;
9147     },
9148
9149     /**
9150      * Refreshes the view.
9151      */
9152     refresh : function(){
9153         var t = this.tpl;
9154         this.clearSelections();
9155         this.el.update("");
9156         var html = [];
9157         var records = this.store.getRange();
9158         if(records.length < 1){
9159             this.el.update(this.emptyText);
9160             return;
9161         }
9162         for(var i = 0, len = records.length; i < len; i++){
9163             var data = this.prepareData(records[i].data, i, records[i]);
9164             html[html.length] = t.apply(data);
9165         }
9166         this.el.update(html.join(""));
9167         this.nodes = this.el.dom.childNodes;
9168         this.updateIndexes(0);
9169     },
9170
9171     /**
9172      * Function to override to reformat the data that is sent to
9173      * the template for each node.
9174      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9175      * a JSON object for an UpdateManager bound view).
9176      */
9177     prepareData : function(data){
9178         return data;
9179     },
9180
9181     onUpdate : function(ds, record){
9182         this.clearSelections();
9183         var index = this.store.indexOf(record);
9184         var n = this.nodes[index];
9185         this.tpl.insertBefore(n, this.prepareData(record.data));
9186         n.parentNode.removeChild(n);
9187         this.updateIndexes(index, index);
9188     },
9189
9190     onAdd : function(ds, records, index){
9191         this.clearSelections();
9192         if(this.nodes.length == 0){
9193             this.refresh();
9194             return;
9195         }
9196         var n = this.nodes[index];
9197         for(var i = 0, len = records.length; i < len; i++){
9198             var d = this.prepareData(records[i].data);
9199             if(n){
9200                 this.tpl.insertBefore(n, d);
9201             }else{
9202                 this.tpl.append(this.el, d);
9203             }
9204         }
9205         this.updateIndexes(index);
9206     },
9207
9208     onRemove : function(ds, record, index){
9209         this.clearSelections();
9210         this.el.dom.removeChild(this.nodes[index]);
9211         this.updateIndexes(index);
9212     },
9213
9214     /**
9215      * Refresh an individual node.
9216      * @param {Number} index
9217      */
9218     refreshNode : function(index){
9219         this.onUpdate(this.store, this.store.getAt(index));
9220     },
9221
9222     updateIndexes : function(startIndex, endIndex){
9223         var ns = this.nodes;
9224         startIndex = startIndex || 0;
9225         endIndex = endIndex || ns.length - 1;
9226         for(var i = startIndex; i <= endIndex; i++){
9227             ns[i].nodeIndex = i;
9228         }
9229     },
9230
9231     /**
9232      * Changes the data store this view uses and refresh the view.
9233      * @param {Store} store
9234      */
9235     setStore : function(store, initial){
9236         if(!initial && this.store){
9237             this.store.un("datachanged", this.refresh);
9238             this.store.un("add", this.onAdd);
9239             this.store.un("remove", this.onRemove);
9240             this.store.un("update", this.onUpdate);
9241             this.store.un("clear", this.refresh);
9242         }
9243         if(store){
9244           
9245             store.on("datachanged", this.refresh, this);
9246             store.on("add", this.onAdd, this);
9247             store.on("remove", this.onRemove, this);
9248             store.on("update", this.onUpdate, this);
9249             store.on("clear", this.refresh, this);
9250         }
9251         
9252         if(store){
9253             this.refresh();
9254         }
9255     },
9256
9257     /**
9258      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9259      * @param {HTMLElement} node
9260      * @return {HTMLElement} The template node
9261      */
9262     findItemFromChild : function(node){
9263         var el = this.el.dom;
9264         if(!node || node.parentNode == el){
9265                     return node;
9266             }
9267             var p = node.parentNode;
9268             while(p && p != el){
9269             if(p.parentNode == el){
9270                 return p;
9271             }
9272             p = p.parentNode;
9273         }
9274             return null;
9275     },
9276
9277     /** @ignore */
9278     onClick : function(e){
9279         var item = this.findItemFromChild(e.getTarget());
9280         if(item){
9281             var index = this.indexOf(item);
9282             if(this.onItemClick(item, index, e) !== false){
9283                 this.fireEvent("click", this, index, item, e);
9284             }
9285         }else{
9286             this.clearSelections();
9287         }
9288     },
9289
9290     /** @ignore */
9291     onContextMenu : function(e){
9292         var item = this.findItemFromChild(e.getTarget());
9293         if(item){
9294             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9295         }
9296     },
9297
9298     /** @ignore */
9299     onDblClick : function(e){
9300         var item = this.findItemFromChild(e.getTarget());
9301         if(item){
9302             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9303         }
9304     },
9305
9306     onItemClick : function(item, index, e){
9307         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9308             return false;
9309         }
9310         if(this.multiSelect || this.singleSelect){
9311             if(this.multiSelect && e.shiftKey && this.lastSelection){
9312                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9313             }else{
9314                 this.select(item, this.multiSelect && e.ctrlKey);
9315                 this.lastSelection = item;
9316             }
9317             e.preventDefault();
9318         }
9319         return true;
9320     },
9321
9322     /**
9323      * Get the number of selected nodes.
9324      * @return {Number}
9325      */
9326     getSelectionCount : function(){
9327         return this.selections.length;
9328     },
9329
9330     /**
9331      * Get the currently selected nodes.
9332      * @return {Array} An array of HTMLElements
9333      */
9334     getSelectedNodes : function(){
9335         return this.selections;
9336     },
9337
9338     /**
9339      * Get the indexes of the selected nodes.
9340      * @return {Array}
9341      */
9342     getSelectedIndexes : function(){
9343         var indexes = [], s = this.selections;
9344         for(var i = 0, len = s.length; i < len; i++){
9345             indexes.push(s[i].nodeIndex);
9346         }
9347         return indexes;
9348     },
9349
9350     /**
9351      * Clear all selections
9352      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9353      */
9354     clearSelections : function(suppressEvent){
9355         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9356             this.cmp.elements = this.selections;
9357             this.cmp.removeClass(this.selectedClass);
9358             this.selections = [];
9359             if(!suppressEvent){
9360                 this.fireEvent("selectionchange", this, this.selections);
9361             }
9362         }
9363     },
9364
9365     /**
9366      * Returns true if the passed node is selected
9367      * @param {HTMLElement/Number} node The node or node index
9368      * @return {Boolean}
9369      */
9370     isSelected : function(node){
9371         var s = this.selections;
9372         if(s.length < 1){
9373             return false;
9374         }
9375         node = this.getNode(node);
9376         return s.indexOf(node) !== -1;
9377     },
9378
9379     /**
9380      * Selects nodes.
9381      * @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
9382      * @param {Boolean} keepExisting (optional) true to keep existing selections
9383      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9384      */
9385     select : function(nodeInfo, keepExisting, suppressEvent){
9386         if(nodeInfo instanceof Array){
9387             if(!keepExisting){
9388                 this.clearSelections(true);
9389             }
9390             for(var i = 0, len = nodeInfo.length; i < len; i++){
9391                 this.select(nodeInfo[i], true, true);
9392             }
9393         } else{
9394             var node = this.getNode(nodeInfo);
9395             if(node && !this.isSelected(node)){
9396                 if(!keepExisting){
9397                     this.clearSelections(true);
9398                 }
9399                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9400                     Roo.fly(node).addClass(this.selectedClass);
9401                     this.selections.push(node);
9402                     if(!suppressEvent){
9403                         this.fireEvent("selectionchange", this, this.selections);
9404                     }
9405                 }
9406             }
9407         }
9408     },
9409
9410     /**
9411      * Gets a template node.
9412      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9413      * @return {HTMLElement} The node or null if it wasn't found
9414      */
9415     getNode : function(nodeInfo){
9416         if(typeof nodeInfo == "string"){
9417             return document.getElementById(nodeInfo);
9418         }else if(typeof nodeInfo == "number"){
9419             return this.nodes[nodeInfo];
9420         }
9421         return nodeInfo;
9422     },
9423
9424     /**
9425      * Gets a range template nodes.
9426      * @param {Number} startIndex
9427      * @param {Number} endIndex
9428      * @return {Array} An array of nodes
9429      */
9430     getNodes : function(start, end){
9431         var ns = this.nodes;
9432         start = start || 0;
9433         end = typeof end == "undefined" ? ns.length - 1 : end;
9434         var nodes = [];
9435         if(start <= end){
9436             for(var i = start; i <= end; i++){
9437                 nodes.push(ns[i]);
9438             }
9439         } else{
9440             for(var i = start; i >= end; i--){
9441                 nodes.push(ns[i]);
9442             }
9443         }
9444         return nodes;
9445     },
9446
9447     /**
9448      * Finds the index of the passed node
9449      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9450      * @return {Number} The index of the node or -1
9451      */
9452     indexOf : function(node){
9453         node = this.getNode(node);
9454         if(typeof node.nodeIndex == "number"){
9455             return node.nodeIndex;
9456         }
9457         var ns = this.nodes;
9458         for(var i = 0, len = ns.length; i < len; i++){
9459             if(ns[i] == node){
9460                 return i;
9461             }
9462         }
9463         return -1;
9464     }
9465 });
9466 /*
9467  * Based on:
9468  * Ext JS Library 1.1.1
9469  * Copyright(c) 2006-2007, Ext JS, LLC.
9470  *
9471  * Originally Released Under LGPL - original licence link has changed is not relivant.
9472  *
9473  * Fork - LGPL
9474  * <script type="text/javascript">
9475  */
9476
9477 /**
9478  * @class Roo.JsonView
9479  * @extends Roo.View
9480  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9481 <pre><code>
9482 var view = new Roo.JsonView({
9483     container: "my-element",
9484     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9485     multiSelect: true, 
9486     jsonRoot: "data" 
9487 });
9488
9489 // listen for node click?
9490 view.on("click", function(vw, index, node, e){
9491     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9492 });
9493
9494 // direct load of JSON data
9495 view.load("foobar.php");
9496
9497 // Example from my blog list
9498 var tpl = new Roo.Template(
9499     '&lt;div class="entry"&gt;' +
9500     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9501     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9502     "&lt;/div&gt;&lt;hr /&gt;"
9503 );
9504
9505 var moreView = new Roo.JsonView({
9506     container :  "entry-list", 
9507     template : tpl,
9508     jsonRoot: "posts"
9509 });
9510 moreView.on("beforerender", this.sortEntries, this);
9511 moreView.load({
9512     url: "/blog/get-posts.php",
9513     params: "allposts=true",
9514     text: "Loading Blog Entries..."
9515 });
9516 </code></pre>
9517
9518 * Note: old code is supported with arguments : (container, template, config)
9519
9520
9521  * @constructor
9522  * Create a new JsonView
9523  * 
9524  * @param {Object} config The config object
9525  * 
9526  */
9527 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9528     
9529     
9530     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9531
9532     var um = this.el.getUpdateManager();
9533     um.setRenderer(this);
9534     um.on("update", this.onLoad, this);
9535     um.on("failure", this.onLoadException, this);
9536
9537     /**
9538      * @event beforerender
9539      * Fires before rendering of the downloaded JSON data.
9540      * @param {Roo.JsonView} this
9541      * @param {Object} data The JSON data loaded
9542      */
9543     /**
9544      * @event load
9545      * Fires when data is loaded.
9546      * @param {Roo.JsonView} this
9547      * @param {Object} data The JSON data loaded
9548      * @param {Object} response The raw Connect response object
9549      */
9550     /**
9551      * @event loadexception
9552      * Fires when loading fails.
9553      * @param {Roo.JsonView} this
9554      * @param {Object} response The raw Connect response object
9555      */
9556     this.addEvents({
9557         'beforerender' : true,
9558         'load' : true,
9559         'loadexception' : true
9560     });
9561 };
9562 Roo.extend(Roo.JsonView, Roo.View, {
9563     /**
9564      * @type {String} The root property in the loaded JSON object that contains the data
9565      */
9566     jsonRoot : "",
9567
9568     /**
9569      * Refreshes the view.
9570      */
9571     refresh : function(){
9572         this.clearSelections();
9573         this.el.update("");
9574         var html = [];
9575         var o = this.jsonData;
9576         if(o && o.length > 0){
9577             for(var i = 0, len = o.length; i < len; i++){
9578                 var data = this.prepareData(o[i], i, o);
9579                 html[html.length] = this.tpl.apply(data);
9580             }
9581         }else{
9582             html.push(this.emptyText);
9583         }
9584         this.el.update(html.join(""));
9585         this.nodes = this.el.dom.childNodes;
9586         this.updateIndexes(0);
9587     },
9588
9589     /**
9590      * 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.
9591      * @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:
9592      <pre><code>
9593      view.load({
9594          url: "your-url.php",
9595          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9596          callback: yourFunction,
9597          scope: yourObject, //(optional scope)
9598          discardUrl: false,
9599          nocache: false,
9600          text: "Loading...",
9601          timeout: 30,
9602          scripts: false
9603      });
9604      </code></pre>
9605      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9606      * 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.
9607      * @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}
9608      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9609      * @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.
9610      */
9611     load : function(){
9612         var um = this.el.getUpdateManager();
9613         um.update.apply(um, arguments);
9614     },
9615
9616     render : function(el, response){
9617         this.clearSelections();
9618         this.el.update("");
9619         var o;
9620         try{
9621             o = Roo.util.JSON.decode(response.responseText);
9622             if(this.jsonRoot){
9623                 
9624                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
9625             }
9626         } catch(e){
9627         }
9628         /**
9629          * The current JSON data or null
9630          */
9631         this.jsonData = o;
9632         this.beforeRender();
9633         this.refresh();
9634     },
9635
9636 /**
9637  * Get the number of records in the current JSON dataset
9638  * @return {Number}
9639  */
9640     getCount : function(){
9641         return this.jsonData ? this.jsonData.length : 0;
9642     },
9643
9644 /**
9645  * Returns the JSON object for the specified node(s)
9646  * @param {HTMLElement/Array} node The node or an array of nodes
9647  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9648  * you get the JSON object for the node
9649  */
9650     getNodeData : function(node){
9651         if(node instanceof Array){
9652             var data = [];
9653             for(var i = 0, len = node.length; i < len; i++){
9654                 data.push(this.getNodeData(node[i]));
9655             }
9656             return data;
9657         }
9658         return this.jsonData[this.indexOf(node)] || null;
9659     },
9660
9661     beforeRender : function(){
9662         this.snapshot = this.jsonData;
9663         if(this.sortInfo){
9664             this.sort.apply(this, this.sortInfo);
9665         }
9666         this.fireEvent("beforerender", this, this.jsonData);
9667     },
9668
9669     onLoad : function(el, o){
9670         this.fireEvent("load", this, this.jsonData, o);
9671     },
9672
9673     onLoadException : function(el, o){
9674         this.fireEvent("loadexception", this, o);
9675     },
9676
9677 /**
9678  * Filter the data by a specific property.
9679  * @param {String} property A property on your JSON objects
9680  * @param {String/RegExp} value Either string that the property values
9681  * should start with, or a RegExp to test against the property
9682  */
9683     filter : function(property, value){
9684         if(this.jsonData){
9685             var data = [];
9686             var ss = this.snapshot;
9687             if(typeof value == "string"){
9688                 var vlen = value.length;
9689                 if(vlen == 0){
9690                     this.clearFilter();
9691                     return;
9692                 }
9693                 value = value.toLowerCase();
9694                 for(var i = 0, len = ss.length; i < len; i++){
9695                     var o = ss[i];
9696                     if(o[property].substr(0, vlen).toLowerCase() == value){
9697                         data.push(o);
9698                     }
9699                 }
9700             } else if(value.exec){ // regex?
9701                 for(var i = 0, len = ss.length; i < len; i++){
9702                     var o = ss[i];
9703                     if(value.test(o[property])){
9704                         data.push(o);
9705                     }
9706                 }
9707             } else{
9708                 return;
9709             }
9710             this.jsonData = data;
9711             this.refresh();
9712         }
9713     },
9714
9715 /**
9716  * Filter by a function. The passed function will be called with each
9717  * object in the current dataset. If the function returns true the value is kept,
9718  * otherwise it is filtered.
9719  * @param {Function} fn
9720  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9721  */
9722     filterBy : function(fn, scope){
9723         if(this.jsonData){
9724             var data = [];
9725             var ss = this.snapshot;
9726             for(var i = 0, len = ss.length; i < len; i++){
9727                 var o = ss[i];
9728                 if(fn.call(scope || this, o)){
9729                     data.push(o);
9730                 }
9731             }
9732             this.jsonData = data;
9733             this.refresh();
9734         }
9735     },
9736
9737 /**
9738  * Clears the current filter.
9739  */
9740     clearFilter : function(){
9741         if(this.snapshot && this.jsonData != this.snapshot){
9742             this.jsonData = this.snapshot;
9743             this.refresh();
9744         }
9745     },
9746
9747
9748 /**
9749  * Sorts the data for this view and refreshes it.
9750  * @param {String} property A property on your JSON objects to sort on
9751  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9752  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9753  */
9754     sort : function(property, dir, sortType){
9755         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9756         if(this.jsonData){
9757             var p = property;
9758             var dsc = dir && dir.toLowerCase() == "desc";
9759             var f = function(o1, o2){
9760                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9761                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9762                 ;
9763                 if(v1 < v2){
9764                     return dsc ? +1 : -1;
9765                 } else if(v1 > v2){
9766                     return dsc ? -1 : +1;
9767                 } else{
9768                     return 0;
9769                 }
9770             };
9771             this.jsonData.sort(f);
9772             this.refresh();
9773             if(this.jsonData != this.snapshot){
9774                 this.snapshot.sort(f);
9775             }
9776         }
9777     }
9778 });/*
9779  * Based on:
9780  * Ext JS Library 1.1.1
9781  * Copyright(c) 2006-2007, Ext JS, LLC.
9782  *
9783  * Originally Released Under LGPL - original licence link has changed is not relivant.
9784  *
9785  * Fork - LGPL
9786  * <script type="text/javascript">
9787  */
9788  
9789
9790 /**
9791  * @class Roo.ColorPalette
9792  * @extends Roo.Component
9793  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9794  * Here's an example of typical usage:
9795  * <pre><code>
9796 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9797 cp.render('my-div');
9798
9799 cp.on('select', function(palette, selColor){
9800     // do something with selColor
9801 });
9802 </code></pre>
9803  * @constructor
9804  * Create a new ColorPalette
9805  * @param {Object} config The config object
9806  */
9807 Roo.ColorPalette = function(config){
9808     Roo.ColorPalette.superclass.constructor.call(this, config);
9809     this.addEvents({
9810         /**
9811              * @event select
9812              * Fires when a color is selected
9813              * @param {ColorPalette} this
9814              * @param {String} color The 6-digit color hex code (without the # symbol)
9815              */
9816         select: true
9817     });
9818
9819     if(this.handler){
9820         this.on("select", this.handler, this.scope, true);
9821     }
9822 };
9823 Roo.extend(Roo.ColorPalette, Roo.Component, {
9824     /**
9825      * @cfg {String} itemCls
9826      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9827      */
9828     itemCls : "x-color-palette",
9829     /**
9830      * @cfg {String} value
9831      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9832      * the hex codes are case-sensitive.
9833      */
9834     value : null,
9835     clickEvent:'click',
9836     // private
9837     ctype: "Roo.ColorPalette",
9838
9839     /**
9840      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9841      */
9842     allowReselect : false,
9843
9844     /**
9845      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9846      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9847      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9848      * of colors with the width setting until the box is symmetrical.</p>
9849      * <p>You can override individual colors if needed:</p>
9850      * <pre><code>
9851 var cp = new Roo.ColorPalette();
9852 cp.colors[0] = "FF0000";  // change the first box to red
9853 </code></pre>
9854
9855 Or you can provide a custom array of your own for complete control:
9856 <pre><code>
9857 var cp = new Roo.ColorPalette();
9858 cp.colors = ["000000", "993300", "333300"];
9859 </code></pre>
9860      * @type Array
9861      */
9862     colors : [
9863         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9864         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9865         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9866         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9867         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9868     ],
9869
9870     // private
9871     onRender : function(container, position){
9872         var t = new Roo.MasterTemplate(
9873             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9874         );
9875         var c = this.colors;
9876         for(var i = 0, len = c.length; i < len; i++){
9877             t.add([c[i]]);
9878         }
9879         var el = document.createElement("div");
9880         el.className = this.itemCls;
9881         t.overwrite(el);
9882         container.dom.insertBefore(el, position);
9883         this.el = Roo.get(el);
9884         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9885         if(this.clickEvent != 'click'){
9886             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9887         }
9888     },
9889
9890     // private
9891     afterRender : function(){
9892         Roo.ColorPalette.superclass.afterRender.call(this);
9893         if(this.value){
9894             var s = this.value;
9895             this.value = null;
9896             this.select(s);
9897         }
9898     },
9899
9900     // private
9901     handleClick : function(e, t){
9902         e.preventDefault();
9903         if(!this.disabled){
9904             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9905             this.select(c.toUpperCase());
9906         }
9907     },
9908
9909     /**
9910      * Selects the specified color in the palette (fires the select event)
9911      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9912      */
9913     select : function(color){
9914         color = color.replace("#", "");
9915         if(color != this.value || this.allowReselect){
9916             var el = this.el;
9917             if(this.value){
9918                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9919             }
9920             el.child("a.color-"+color).addClass("x-color-palette-sel");
9921             this.value = color;
9922             this.fireEvent("select", this, color);
9923         }
9924     }
9925 });/*
9926  * Based on:
9927  * Ext JS Library 1.1.1
9928  * Copyright(c) 2006-2007, Ext JS, LLC.
9929  *
9930  * Originally Released Under LGPL - original licence link has changed is not relivant.
9931  *
9932  * Fork - LGPL
9933  * <script type="text/javascript">
9934  */
9935  
9936 /**
9937  * @class Roo.DatePicker
9938  * @extends Roo.Component
9939  * Simple date picker class.
9940  * @constructor
9941  * Create a new DatePicker
9942  * @param {Object} config The config object
9943  */
9944 Roo.DatePicker = function(config){
9945     Roo.DatePicker.superclass.constructor.call(this, config);
9946
9947     this.value = config && config.value ?
9948                  config.value.clearTime() : new Date().clearTime();
9949
9950     this.addEvents({
9951         /**
9952              * @event select
9953              * Fires when a date is selected
9954              * @param {DatePicker} this
9955              * @param {Date} date The selected date
9956              */
9957         select: true
9958     });
9959
9960     if(this.handler){
9961         this.on("select", this.handler,  this.scope || this);
9962     }
9963     // build the disabledDatesRE
9964     if(!this.disabledDatesRE && this.disabledDates){
9965         var dd = this.disabledDates;
9966         var re = "(?:";
9967         for(var i = 0; i < dd.length; i++){
9968             re += dd[i];
9969             if(i != dd.length-1) re += "|";
9970         }
9971         this.disabledDatesRE = new RegExp(re + ")");
9972     }
9973 };
9974
9975 Roo.extend(Roo.DatePicker, Roo.Component, {
9976     /**
9977      * @cfg {String} todayText
9978      * The text to display on the button that selects the current date (defaults to "Today")
9979      */
9980     todayText : "Today",
9981     /**
9982      * @cfg {String} okText
9983      * The text to display on the ok button
9984      */
9985     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9986     /**
9987      * @cfg {String} cancelText
9988      * The text to display on the cancel button
9989      */
9990     cancelText : "Cancel",
9991     /**
9992      * @cfg {String} todayTip
9993      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9994      */
9995     todayTip : "{0} (Spacebar)",
9996     /**
9997      * @cfg {Date} minDate
9998      * Minimum allowable date (JavaScript date object, defaults to null)
9999      */
10000     minDate : null,
10001     /**
10002      * @cfg {Date} maxDate
10003      * Maximum allowable date (JavaScript date object, defaults to null)
10004      */
10005     maxDate : null,
10006     /**
10007      * @cfg {String} minText
10008      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10009      */
10010     minText : "This date is before the minimum date",
10011     /**
10012      * @cfg {String} maxText
10013      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10014      */
10015     maxText : "This date is after the maximum date",
10016     /**
10017      * @cfg {String} format
10018      * The default date format string which can be overriden for localization support.  The format must be
10019      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10020      */
10021     format : "m/d/y",
10022     /**
10023      * @cfg {Array} disabledDays
10024      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10025      */
10026     disabledDays : null,
10027     /**
10028      * @cfg {String} disabledDaysText
10029      * The tooltip to display when the date falls on a disabled day (defaults to "")
10030      */
10031     disabledDaysText : "",
10032     /**
10033      * @cfg {RegExp} disabledDatesRE
10034      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10035      */
10036     disabledDatesRE : null,
10037     /**
10038      * @cfg {String} disabledDatesText
10039      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10040      */
10041     disabledDatesText : "",
10042     /**
10043      * @cfg {Boolean} constrainToViewport
10044      * True to constrain the date picker to the viewport (defaults to true)
10045      */
10046     constrainToViewport : true,
10047     /**
10048      * @cfg {Array} monthNames
10049      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10050      */
10051     monthNames : Date.monthNames,
10052     /**
10053      * @cfg {Array} dayNames
10054      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10055      */
10056     dayNames : Date.dayNames,
10057     /**
10058      * @cfg {String} nextText
10059      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10060      */
10061     nextText: 'Next Month (Control+Right)',
10062     /**
10063      * @cfg {String} prevText
10064      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10065      */
10066     prevText: 'Previous Month (Control+Left)',
10067     /**
10068      * @cfg {String} monthYearText
10069      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10070      */
10071     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10072     /**
10073      * @cfg {Number} startDay
10074      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10075      */
10076     startDay : 0,
10077     /**
10078      * @cfg {Bool} showClear
10079      * Show a clear button (usefull for date form elements that can be blank.)
10080      */
10081     
10082     showClear: false,
10083     
10084     /**
10085      * Sets the value of the date field
10086      * @param {Date} value The date to set
10087      */
10088     setValue : function(value){
10089         var old = this.value;
10090         this.value = value.clearTime(true);
10091         if(this.el){
10092             this.update(this.value);
10093         }
10094     },
10095
10096     /**
10097      * Gets the current selected value of the date field
10098      * @return {Date} The selected date
10099      */
10100     getValue : function(){
10101         return this.value;
10102     },
10103
10104     // private
10105     focus : function(){
10106         if(this.el){
10107             this.update(this.activeDate);
10108         }
10109     },
10110
10111     // private
10112     onRender : function(container, position){
10113         var m = [
10114              '<table cellspacing="0">',
10115                 '<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>',
10116                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10117         var dn = this.dayNames;
10118         for(var i = 0; i < 7; i++){
10119             var d = this.startDay+i;
10120             if(d > 6){
10121                 d = d-7;
10122             }
10123             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10124         }
10125         m[m.length] = "</tr></thead><tbody><tr>";
10126         for(var i = 0; i < 42; i++) {
10127             if(i % 7 == 0 && i != 0){
10128                 m[m.length] = "</tr><tr>";
10129             }
10130             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10131         }
10132         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10133             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10134
10135         var el = document.createElement("div");
10136         el.className = "x-date-picker";
10137         el.innerHTML = m.join("");
10138
10139         container.dom.insertBefore(el, position);
10140
10141         this.el = Roo.get(el);
10142         this.eventEl = Roo.get(el.firstChild);
10143
10144         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10145             handler: this.showPrevMonth,
10146             scope: this,
10147             preventDefault:true,
10148             stopDefault:true
10149         });
10150
10151         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10152             handler: this.showNextMonth,
10153             scope: this,
10154             preventDefault:true,
10155             stopDefault:true
10156         });
10157
10158         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10159
10160         this.monthPicker = this.el.down('div.x-date-mp');
10161         this.monthPicker.enableDisplayMode('block');
10162         
10163         var kn = new Roo.KeyNav(this.eventEl, {
10164             "left" : function(e){
10165                 e.ctrlKey ?
10166                     this.showPrevMonth() :
10167                     this.update(this.activeDate.add("d", -1));
10168             },
10169
10170             "right" : function(e){
10171                 e.ctrlKey ?
10172                     this.showNextMonth() :
10173                     this.update(this.activeDate.add("d", 1));
10174             },
10175
10176             "up" : function(e){
10177                 e.ctrlKey ?
10178                     this.showNextYear() :
10179                     this.update(this.activeDate.add("d", -7));
10180             },
10181
10182             "down" : function(e){
10183                 e.ctrlKey ?
10184                     this.showPrevYear() :
10185                     this.update(this.activeDate.add("d", 7));
10186             },
10187
10188             "pageUp" : function(e){
10189                 this.showNextMonth();
10190             },
10191
10192             "pageDown" : function(e){
10193                 this.showPrevMonth();
10194             },
10195
10196             "enter" : function(e){
10197                 e.stopPropagation();
10198                 return true;
10199             },
10200
10201             scope : this
10202         });
10203
10204         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10205
10206         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10207
10208         this.el.unselectable();
10209         
10210         this.cells = this.el.select("table.x-date-inner tbody td");
10211         this.textNodes = this.el.query("table.x-date-inner tbody span");
10212
10213         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10214             text: "&#160;",
10215             tooltip: this.monthYearText
10216         });
10217
10218         this.mbtn.on('click', this.showMonthPicker, this);
10219         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10220
10221
10222         var today = (new Date()).dateFormat(this.format);
10223         
10224         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10225         baseTb.add({
10226             text: String.format(this.todayText, today),
10227             tooltip: String.format(this.todayTip, today),
10228             handler: this.selectToday,
10229             scope: this
10230         });
10231         
10232         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10233             
10234         //});
10235         if (this.showClear) {
10236             
10237             baseTb.add( new Roo.Toolbar.Fill());
10238             baseTb.add({
10239                 text: '&#160;',
10240                 cls: 'x-btn-icon x-btn-clear',
10241                 handler: function() {
10242                     //this.value = '';
10243                     this.fireEvent("select", this, '');
10244                 },
10245                 scope: this
10246             });
10247         }
10248         
10249         
10250         if(Roo.isIE){
10251             this.el.repaint();
10252         }
10253         this.update(this.value);
10254     },
10255
10256     createMonthPicker : function(){
10257         if(!this.monthPicker.dom.firstChild){
10258             var buf = ['<table border="0" cellspacing="0">'];
10259             for(var i = 0; i < 6; i++){
10260                 buf.push(
10261                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10262                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10263                     i == 0 ?
10264                     '<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>' :
10265                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10266                 );
10267             }
10268             buf.push(
10269                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10270                     this.okText,
10271                     '</button><button type="button" class="x-date-mp-cancel">',
10272                     this.cancelText,
10273                     '</button></td></tr>',
10274                 '</table>'
10275             );
10276             this.monthPicker.update(buf.join(''));
10277             this.monthPicker.on('click', this.onMonthClick, this);
10278             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10279
10280             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10281             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10282
10283             this.mpMonths.each(function(m, a, i){
10284                 i += 1;
10285                 if((i%2) == 0){
10286                     m.dom.xmonth = 5 + Math.round(i * .5);
10287                 }else{
10288                     m.dom.xmonth = Math.round((i-1) * .5);
10289                 }
10290             });
10291         }
10292     },
10293
10294     showMonthPicker : function(){
10295         this.createMonthPicker();
10296         var size = this.el.getSize();
10297         this.monthPicker.setSize(size);
10298         this.monthPicker.child('table').setSize(size);
10299
10300         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10301         this.updateMPMonth(this.mpSelMonth);
10302         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10303         this.updateMPYear(this.mpSelYear);
10304
10305         this.monthPicker.slideIn('t', {duration:.2});
10306     },
10307
10308     updateMPYear : function(y){
10309         this.mpyear = y;
10310         var ys = this.mpYears.elements;
10311         for(var i = 1; i <= 10; i++){
10312             var td = ys[i-1], y2;
10313             if((i%2) == 0){
10314                 y2 = y + Math.round(i * .5);
10315                 td.firstChild.innerHTML = y2;
10316                 td.xyear = y2;
10317             }else{
10318                 y2 = y - (5-Math.round(i * .5));
10319                 td.firstChild.innerHTML = y2;
10320                 td.xyear = y2;
10321             }
10322             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10323         }
10324     },
10325
10326     updateMPMonth : function(sm){
10327         this.mpMonths.each(function(m, a, i){
10328             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10329         });
10330     },
10331
10332     selectMPMonth: function(m){
10333         
10334     },
10335
10336     onMonthClick : function(e, t){
10337         e.stopEvent();
10338         var el = new Roo.Element(t), pn;
10339         if(el.is('button.x-date-mp-cancel')){
10340             this.hideMonthPicker();
10341         }
10342         else if(el.is('button.x-date-mp-ok')){
10343             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10344             this.hideMonthPicker();
10345         }
10346         else if(pn = el.up('td.x-date-mp-month', 2)){
10347             this.mpMonths.removeClass('x-date-mp-sel');
10348             pn.addClass('x-date-mp-sel');
10349             this.mpSelMonth = pn.dom.xmonth;
10350         }
10351         else if(pn = el.up('td.x-date-mp-year', 2)){
10352             this.mpYears.removeClass('x-date-mp-sel');
10353             pn.addClass('x-date-mp-sel');
10354             this.mpSelYear = pn.dom.xyear;
10355         }
10356         else if(el.is('a.x-date-mp-prev')){
10357             this.updateMPYear(this.mpyear-10);
10358         }
10359         else if(el.is('a.x-date-mp-next')){
10360             this.updateMPYear(this.mpyear+10);
10361         }
10362     },
10363
10364     onMonthDblClick : function(e, t){
10365         e.stopEvent();
10366         var el = new Roo.Element(t), pn;
10367         if(pn = el.up('td.x-date-mp-month', 2)){
10368             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10369             this.hideMonthPicker();
10370         }
10371         else if(pn = el.up('td.x-date-mp-year', 2)){
10372             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10373             this.hideMonthPicker();
10374         }
10375     },
10376
10377     hideMonthPicker : function(disableAnim){
10378         if(this.monthPicker){
10379             if(disableAnim === true){
10380                 this.monthPicker.hide();
10381             }else{
10382                 this.monthPicker.slideOut('t', {duration:.2});
10383             }
10384         }
10385     },
10386
10387     // private
10388     showPrevMonth : function(e){
10389         this.update(this.activeDate.add("mo", -1));
10390     },
10391
10392     // private
10393     showNextMonth : function(e){
10394         this.update(this.activeDate.add("mo", 1));
10395     },
10396
10397     // private
10398     showPrevYear : function(){
10399         this.update(this.activeDate.add("y", -1));
10400     },
10401
10402     // private
10403     showNextYear : function(){
10404         this.update(this.activeDate.add("y", 1));
10405     },
10406
10407     // private
10408     handleMouseWheel : function(e){
10409         var delta = e.getWheelDelta();
10410         if(delta > 0){
10411             this.showPrevMonth();
10412             e.stopEvent();
10413         } else if(delta < 0){
10414             this.showNextMonth();
10415             e.stopEvent();
10416         }
10417     },
10418
10419     // private
10420     handleDateClick : function(e, t){
10421         e.stopEvent();
10422         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10423             this.setValue(new Date(t.dateValue));
10424             this.fireEvent("select", this, this.value);
10425         }
10426     },
10427
10428     // private
10429     selectToday : function(){
10430         this.setValue(new Date().clearTime());
10431         this.fireEvent("select", this, this.value);
10432     },
10433
10434     // private
10435     update : function(date){
10436         var vd = this.activeDate;
10437         this.activeDate = date;
10438         if(vd && this.el){
10439             var t = date.getTime();
10440             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10441                 this.cells.removeClass("x-date-selected");
10442                 this.cells.each(function(c){
10443                    if(c.dom.firstChild.dateValue == t){
10444                        c.addClass("x-date-selected");
10445                        setTimeout(function(){
10446                             try{c.dom.firstChild.focus();}catch(e){}
10447                        }, 50);
10448                        return false;
10449                    }
10450                 });
10451                 return;
10452             }
10453         }
10454         var days = date.getDaysInMonth();
10455         var firstOfMonth = date.getFirstDateOfMonth();
10456         var startingPos = firstOfMonth.getDay()-this.startDay;
10457
10458         if(startingPos <= this.startDay){
10459             startingPos += 7;
10460         }
10461
10462         var pm = date.add("mo", -1);
10463         var prevStart = pm.getDaysInMonth()-startingPos;
10464
10465         var cells = this.cells.elements;
10466         var textEls = this.textNodes;
10467         days += startingPos;
10468
10469         // convert everything to numbers so it's fast
10470         var day = 86400000;
10471         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10472         var today = new Date().clearTime().getTime();
10473         var sel = date.clearTime().getTime();
10474         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10475         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10476         var ddMatch = this.disabledDatesRE;
10477         var ddText = this.disabledDatesText;
10478         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10479         var ddaysText = this.disabledDaysText;
10480         var format = this.format;
10481
10482         var setCellClass = function(cal, cell){
10483             cell.title = "";
10484             var t = d.getTime();
10485             cell.firstChild.dateValue = t;
10486             if(t == today){
10487                 cell.className += " x-date-today";
10488                 cell.title = cal.todayText;
10489             }
10490             if(t == sel){
10491                 cell.className += " x-date-selected";
10492                 setTimeout(function(){
10493                     try{cell.firstChild.focus();}catch(e){}
10494                 }, 50);
10495             }
10496             // disabling
10497             if(t < min) {
10498                 cell.className = " x-date-disabled";
10499                 cell.title = cal.minText;
10500                 return;
10501             }
10502             if(t > max) {
10503                 cell.className = " x-date-disabled";
10504                 cell.title = cal.maxText;
10505                 return;
10506             }
10507             if(ddays){
10508                 if(ddays.indexOf(d.getDay()) != -1){
10509                     cell.title = ddaysText;
10510                     cell.className = " x-date-disabled";
10511                 }
10512             }
10513             if(ddMatch && format){
10514                 var fvalue = d.dateFormat(format);
10515                 if(ddMatch.test(fvalue)){
10516                     cell.title = ddText.replace("%0", fvalue);
10517                     cell.className = " x-date-disabled";
10518                 }
10519             }
10520         };
10521
10522         var i = 0;
10523         for(; i < startingPos; i++) {
10524             textEls[i].innerHTML = (++prevStart);
10525             d.setDate(d.getDate()+1);
10526             cells[i].className = "x-date-prevday";
10527             setCellClass(this, cells[i]);
10528         }
10529         for(; i < days; i++){
10530             intDay = i - startingPos + 1;
10531             textEls[i].innerHTML = (intDay);
10532             d.setDate(d.getDate()+1);
10533             cells[i].className = "x-date-active";
10534             setCellClass(this, cells[i]);
10535         }
10536         var extraDays = 0;
10537         for(; i < 42; i++) {
10538              textEls[i].innerHTML = (++extraDays);
10539              d.setDate(d.getDate()+1);
10540              cells[i].className = "x-date-nextday";
10541              setCellClass(this, cells[i]);
10542         }
10543
10544         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10545
10546         if(!this.internalRender){
10547             var main = this.el.dom.firstChild;
10548             var w = main.offsetWidth;
10549             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10550             Roo.fly(main).setWidth(w);
10551             this.internalRender = true;
10552             // opera does not respect the auto grow header center column
10553             // then, after it gets a width opera refuses to recalculate
10554             // without a second pass
10555             if(Roo.isOpera && !this.secondPass){
10556                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10557                 this.secondPass = true;
10558                 this.update.defer(10, this, [date]);
10559             }
10560         }
10561     }
10562 });/*
10563  * Based on:
10564  * Ext JS Library 1.1.1
10565  * Copyright(c) 2006-2007, Ext JS, LLC.
10566  *
10567  * Originally Released Under LGPL - original licence link has changed is not relivant.
10568  *
10569  * Fork - LGPL
10570  * <script type="text/javascript">
10571  */
10572 /**
10573  * @class Roo.TabPanel
10574  * @extends Roo.util.Observable
10575  * A lightweight tab container.
10576  * <br><br>
10577  * Usage:
10578  * <pre><code>
10579 // basic tabs 1, built from existing content
10580 var tabs = new Roo.TabPanel("tabs1");
10581 tabs.addTab("script", "View Script");
10582 tabs.addTab("markup", "View Markup");
10583 tabs.activate("script");
10584
10585 // more advanced tabs, built from javascript
10586 var jtabs = new Roo.TabPanel("jtabs");
10587 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10588
10589 // set up the UpdateManager
10590 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10591 var updater = tab2.getUpdateManager();
10592 updater.setDefaultUrl("ajax1.htm");
10593 tab2.on('activate', updater.refresh, updater, true);
10594
10595 // Use setUrl for Ajax loading
10596 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10597 tab3.setUrl("ajax2.htm", null, true);
10598
10599 // Disabled tab
10600 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10601 tab4.disable();
10602
10603 jtabs.activate("jtabs-1");
10604  * </code></pre>
10605  * @constructor
10606  * Create a new TabPanel.
10607  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10608  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10609  */
10610 Roo.TabPanel = function(container, config){
10611     /**
10612     * The container element for this TabPanel.
10613     * @type Roo.Element
10614     */
10615     this.el = Roo.get(container, true);
10616     if(config){
10617         if(typeof config == "boolean"){
10618             this.tabPosition = config ? "bottom" : "top";
10619         }else{
10620             Roo.apply(this, config);
10621         }
10622     }
10623     if(this.tabPosition == "bottom"){
10624         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10625         this.el.addClass("x-tabs-bottom");
10626     }
10627     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10628     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10629     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10630     if(Roo.isIE){
10631         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10632     }
10633     if(this.tabPosition != "bottom"){
10634     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10635      * @type Roo.Element
10636      */
10637       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10638       this.el.addClass("x-tabs-top");
10639     }
10640     this.items = [];
10641
10642     this.bodyEl.setStyle("position", "relative");
10643
10644     this.active = null;
10645     this.activateDelegate = this.activate.createDelegate(this);
10646
10647     this.addEvents({
10648         /**
10649          * @event tabchange
10650          * Fires when the active tab changes
10651          * @param {Roo.TabPanel} this
10652          * @param {Roo.TabPanelItem} activePanel The new active tab
10653          */
10654         "tabchange": true,
10655         /**
10656          * @event beforetabchange
10657          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10658          * @param {Roo.TabPanel} this
10659          * @param {Object} e Set cancel to true on this object to cancel the tab change
10660          * @param {Roo.TabPanelItem} tab The tab being changed to
10661          */
10662         "beforetabchange" : true
10663     });
10664
10665     Roo.EventManager.onWindowResize(this.onResize, this);
10666     this.cpad = this.el.getPadding("lr");
10667     this.hiddenCount = 0;
10668
10669     Roo.TabPanel.superclass.constructor.call(this);
10670 };
10671
10672 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10673         /*
10674          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10675          */
10676     tabPosition : "top",
10677         /*
10678          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10679          */
10680     currentTabWidth : 0,
10681         /*
10682          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10683          */
10684     minTabWidth : 40,
10685         /*
10686          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10687          */
10688     maxTabWidth : 250,
10689         /*
10690          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10691          */
10692     preferredTabWidth : 175,
10693         /*
10694          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10695          */
10696     resizeTabs : false,
10697         /*
10698          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10699          */
10700     monitorResize : true,
10701
10702     /**
10703      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10704      * @param {String} id The id of the div to use <b>or create</b>
10705      * @param {String} text The text for the tab
10706      * @param {String} content (optional) Content to put in the TabPanelItem body
10707      * @param {Boolean} closable (optional) True to create a close icon on the tab
10708      * @return {Roo.TabPanelItem} The created TabPanelItem
10709      */
10710     addTab : function(id, text, content, closable){
10711         var item = new Roo.TabPanelItem(this, id, text, closable);
10712         this.addTabItem(item);
10713         if(content){
10714             item.setContent(content);
10715         }
10716         return item;
10717     },
10718
10719     /**
10720      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10721      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10722      * @return {Roo.TabPanelItem}
10723      */
10724     getTab : function(id){
10725         return this.items[id];
10726     },
10727
10728     /**
10729      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10730      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10731      */
10732     hideTab : function(id){
10733         var t = this.items[id];
10734         if(!t.isHidden()){
10735            t.setHidden(true);
10736            this.hiddenCount++;
10737            this.autoSizeTabs();
10738         }
10739     },
10740
10741     /**
10742      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10743      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10744      */
10745     unhideTab : function(id){
10746         var t = this.items[id];
10747         if(t.isHidden()){
10748            t.setHidden(false);
10749            this.hiddenCount--;
10750            this.autoSizeTabs();
10751         }
10752     },
10753
10754     /**
10755      * Adds an existing {@link Roo.TabPanelItem}.
10756      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10757      */
10758     addTabItem : function(item){
10759         this.items[item.id] = item;
10760         this.items.push(item);
10761         if(this.resizeTabs){
10762            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10763            this.autoSizeTabs();
10764         }else{
10765             item.autoSize();
10766         }
10767     },
10768
10769     /**
10770      * Removes a {@link Roo.TabPanelItem}.
10771      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10772      */
10773     removeTab : function(id){
10774         var items = this.items;
10775         var tab = items[id];
10776         if(!tab) return;
10777         var index = items.indexOf(tab);
10778         if(this.active == tab && items.length > 1){
10779             var newTab = this.getNextAvailable(index);
10780             if(newTab)newTab.activate();
10781         }
10782         this.stripEl.dom.removeChild(tab.pnode.dom);
10783         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10784             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10785         }
10786         items.splice(index, 1);
10787         delete this.items[tab.id];
10788         tab.fireEvent("close", tab);
10789         tab.purgeListeners();
10790         this.autoSizeTabs();
10791     },
10792
10793     getNextAvailable : function(start){
10794         var items = this.items;
10795         var index = start;
10796         // look for a next tab that will slide over to
10797         // replace the one being removed
10798         while(index < items.length){
10799             var item = items[++index];
10800             if(item && !item.isHidden()){
10801                 return item;
10802             }
10803         }
10804         // if one isn't found select the previous tab (on the left)
10805         index = start;
10806         while(index >= 0){
10807             var item = items[--index];
10808             if(item && !item.isHidden()){
10809                 return item;
10810             }
10811         }
10812         return null;
10813     },
10814
10815     /**
10816      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10817      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10818      */
10819     disableTab : function(id){
10820         var tab = this.items[id];
10821         if(tab && this.active != tab){
10822             tab.disable();
10823         }
10824     },
10825
10826     /**
10827      * Enables a {@link Roo.TabPanelItem} that is disabled.
10828      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10829      */
10830     enableTab : function(id){
10831         var tab = this.items[id];
10832         tab.enable();
10833     },
10834
10835     /**
10836      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10837      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10838      * @return {Roo.TabPanelItem} The TabPanelItem.
10839      */
10840     activate : function(id){
10841         var tab = this.items[id];
10842         if(!tab){
10843             return null;
10844         }
10845         if(tab == this.active || tab.disabled){
10846             return tab;
10847         }
10848         var e = {};
10849         this.fireEvent("beforetabchange", this, e, tab);
10850         if(e.cancel !== true && !tab.disabled){
10851             if(this.active){
10852                 this.active.hide();
10853             }
10854             this.active = this.items[id];
10855             this.active.show();
10856             this.fireEvent("tabchange", this, this.active);
10857         }
10858         return tab;
10859     },
10860
10861     /**
10862      * Gets the active {@link Roo.TabPanelItem}.
10863      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10864      */
10865     getActiveTab : function(){
10866         return this.active;
10867     },
10868
10869     /**
10870      * Updates the tab body element to fit the height of the container element
10871      * for overflow scrolling
10872      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10873      */
10874     syncHeight : function(targetHeight){
10875         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10876         var bm = this.bodyEl.getMargins();
10877         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10878         this.bodyEl.setHeight(newHeight);
10879         return newHeight;
10880     },
10881
10882     onResize : function(){
10883         if(this.monitorResize){
10884             this.autoSizeTabs();
10885         }
10886     },
10887
10888     /**
10889      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10890      */
10891     beginUpdate : function(){
10892         this.updating = true;
10893     },
10894
10895     /**
10896      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10897      */
10898     endUpdate : function(){
10899         this.updating = false;
10900         this.autoSizeTabs();
10901     },
10902
10903     /**
10904      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10905      */
10906     autoSizeTabs : function(){
10907         var count = this.items.length;
10908         var vcount = count - this.hiddenCount;
10909         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10910         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10911         var availWidth = Math.floor(w / vcount);
10912         var b = this.stripBody;
10913         if(b.getWidth() > w){
10914             var tabs = this.items;
10915             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10916             if(availWidth < this.minTabWidth){
10917                 /*if(!this.sleft){    // incomplete scrolling code
10918                     this.createScrollButtons();
10919                 }
10920                 this.showScroll();
10921                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10922             }
10923         }else{
10924             if(this.currentTabWidth < this.preferredTabWidth){
10925                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10926             }
10927         }
10928     },
10929
10930     /**
10931      * Returns the number of tabs in this TabPanel.
10932      * @return {Number}
10933      */
10934      getCount : function(){
10935          return this.items.length;
10936      },
10937
10938     /**
10939      * Resizes all the tabs to the passed width
10940      * @param {Number} The new width
10941      */
10942     setTabWidth : function(width){
10943         this.currentTabWidth = width;
10944         for(var i = 0, len = this.items.length; i < len; i++) {
10945                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10946         }
10947     },
10948
10949     /**
10950      * Destroys this TabPanel
10951      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10952      */
10953     destroy : function(removeEl){
10954         Roo.EventManager.removeResizeListener(this.onResize, this);
10955         for(var i = 0, len = this.items.length; i < len; i++){
10956             this.items[i].purgeListeners();
10957         }
10958         if(removeEl === true){
10959             this.el.update("");
10960             this.el.remove();
10961         }
10962     }
10963 });
10964
10965 /**
10966  * @class Roo.TabPanelItem
10967  * @extends Roo.util.Observable
10968  * Represents an individual item (tab plus body) in a TabPanel.
10969  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10970  * @param {String} id The id of this TabPanelItem
10971  * @param {String} text The text for the tab of this TabPanelItem
10972  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10973  */
10974 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10975     /**
10976      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10977      * @type Roo.TabPanel
10978      */
10979     this.tabPanel = tabPanel;
10980     /**
10981      * The id for this TabPanelItem
10982      * @type String
10983      */
10984     this.id = id;
10985     /** @private */
10986     this.disabled = false;
10987     /** @private */
10988     this.text = text;
10989     /** @private */
10990     this.loaded = false;
10991     this.closable = closable;
10992
10993     /**
10994      * The body element for this TabPanelItem.
10995      * @type Roo.Element
10996      */
10997     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10998     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10999     this.bodyEl.setStyle("display", "block");
11000     this.bodyEl.setStyle("zoom", "1");
11001     this.hideAction();
11002
11003     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11004     /** @private */
11005     this.el = Roo.get(els.el, true);
11006     this.inner = Roo.get(els.inner, true);
11007     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11008     this.pnode = Roo.get(els.el.parentNode, true);
11009     this.el.on("mousedown", this.onTabMouseDown, this);
11010     this.el.on("click", this.onTabClick, this);
11011     /** @private */
11012     if(closable){
11013         var c = Roo.get(els.close, true);
11014         c.dom.title = this.closeText;
11015         c.addClassOnOver("close-over");
11016         c.on("click", this.closeClick, this);
11017      }
11018
11019     this.addEvents({
11020          /**
11021          * @event activate
11022          * Fires when this tab becomes the active tab.
11023          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11024          * @param {Roo.TabPanelItem} this
11025          */
11026         "activate": true,
11027         /**
11028          * @event beforeclose
11029          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11030          * @param {Roo.TabPanelItem} this
11031          * @param {Object} e Set cancel to true on this object to cancel the close.
11032          */
11033         "beforeclose": true,
11034         /**
11035          * @event close
11036          * Fires when this tab is closed.
11037          * @param {Roo.TabPanelItem} this
11038          */
11039          "close": true,
11040         /**
11041          * @event deactivate
11042          * Fires when this tab is no longer the active tab.
11043          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11044          * @param {Roo.TabPanelItem} this
11045          */
11046          "deactivate" : true
11047     });
11048     this.hidden = false;
11049
11050     Roo.TabPanelItem.superclass.constructor.call(this);
11051 };
11052
11053 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11054     purgeListeners : function(){
11055        Roo.util.Observable.prototype.purgeListeners.call(this);
11056        this.el.removeAllListeners();
11057     },
11058     /**
11059      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11060      */
11061     show : function(){
11062         this.pnode.addClass("on");
11063         this.showAction();
11064         if(Roo.isOpera){
11065             this.tabPanel.stripWrap.repaint();
11066         }
11067         this.fireEvent("activate", this.tabPanel, this);
11068     },
11069
11070     /**
11071      * Returns true if this tab is the active tab.
11072      * @return {Boolean}
11073      */
11074     isActive : function(){
11075         return this.tabPanel.getActiveTab() == this;
11076     },
11077
11078     /**
11079      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11080      */
11081     hide : function(){
11082         this.pnode.removeClass("on");
11083         this.hideAction();
11084         this.fireEvent("deactivate", this.tabPanel, this);
11085     },
11086
11087     hideAction : function(){
11088         this.bodyEl.hide();
11089         this.bodyEl.setStyle("position", "absolute");
11090         this.bodyEl.setLeft("-20000px");
11091         this.bodyEl.setTop("-20000px");
11092     },
11093
11094     showAction : function(){
11095         this.bodyEl.setStyle("position", "relative");
11096         this.bodyEl.setTop("");
11097         this.bodyEl.setLeft("");
11098         this.bodyEl.show();
11099     },
11100
11101     /**
11102      * Set the tooltip for the tab.
11103      * @param {String} tooltip The tab's tooltip
11104      */
11105     setTooltip : function(text){
11106         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11107             this.textEl.dom.qtip = text;
11108             this.textEl.dom.removeAttribute('title');
11109         }else{
11110             this.textEl.dom.title = text;
11111         }
11112     },
11113
11114     onTabClick : function(e){
11115         e.preventDefault();
11116         this.tabPanel.activate(this.id);
11117     },
11118
11119     onTabMouseDown : function(e){
11120         e.preventDefault();
11121         this.tabPanel.activate(this.id);
11122     },
11123
11124     getWidth : function(){
11125         return this.inner.getWidth();
11126     },
11127
11128     setWidth : function(width){
11129         var iwidth = width - this.pnode.getPadding("lr");
11130         this.inner.setWidth(iwidth);
11131         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11132         this.pnode.setWidth(width);
11133     },
11134
11135     /**
11136      * Show or hide the tab
11137      * @param {Boolean} hidden True to hide or false to show.
11138      */
11139     setHidden : function(hidden){
11140         this.hidden = hidden;
11141         this.pnode.setStyle("display", hidden ? "none" : "");
11142     },
11143
11144     /**
11145      * Returns true if this tab is "hidden"
11146      * @return {Boolean}
11147      */
11148     isHidden : function(){
11149         return this.hidden;
11150     },
11151
11152     /**
11153      * Returns the text for this tab
11154      * @return {String}
11155      */
11156     getText : function(){
11157         return this.text;
11158     },
11159
11160     autoSize : function(){
11161         //this.el.beginMeasure();
11162         this.textEl.setWidth(1);
11163         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11164         //this.el.endMeasure();
11165     },
11166
11167     /**
11168      * Sets the text for the tab (Note: this also sets the tooltip text)
11169      * @param {String} text The tab's text and tooltip
11170      */
11171     setText : function(text){
11172         this.text = text;
11173         this.textEl.update(text);
11174         this.setTooltip(text);
11175         if(!this.tabPanel.resizeTabs){
11176             this.autoSize();
11177         }
11178     },
11179     /**
11180      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11181      */
11182     activate : function(){
11183         this.tabPanel.activate(this.id);
11184     },
11185
11186     /**
11187      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11188      */
11189     disable : function(){
11190         if(this.tabPanel.active != this){
11191             this.disabled = true;
11192             this.pnode.addClass("disabled");
11193         }
11194     },
11195
11196     /**
11197      * Enables this TabPanelItem if it was previously disabled.
11198      */
11199     enable : function(){
11200         this.disabled = false;
11201         this.pnode.removeClass("disabled");
11202     },
11203
11204     /**
11205      * Sets the content for this TabPanelItem.
11206      * @param {String} content The content
11207      * @param {Boolean} loadScripts true to look for and load scripts
11208      */
11209     setContent : function(content, loadScripts){
11210         this.bodyEl.update(content, loadScripts);
11211     },
11212
11213     /**
11214      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11215      * @return {Roo.UpdateManager} The UpdateManager
11216      */
11217     getUpdateManager : function(){
11218         return this.bodyEl.getUpdateManager();
11219     },
11220
11221     /**
11222      * Set a URL to be used to load the content for this TabPanelItem.
11223      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11224      * @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)
11225      * @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)
11226      * @return {Roo.UpdateManager} The UpdateManager
11227      */
11228     setUrl : function(url, params, loadOnce){
11229         if(this.refreshDelegate){
11230             this.un('activate', this.refreshDelegate);
11231         }
11232         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11233         this.on("activate", this.refreshDelegate);
11234         return this.bodyEl.getUpdateManager();
11235     },
11236
11237     /** @private */
11238     _handleRefresh : function(url, params, loadOnce){
11239         if(!loadOnce || !this.loaded){
11240             var updater = this.bodyEl.getUpdateManager();
11241             updater.update(url, params, this._setLoaded.createDelegate(this));
11242         }
11243     },
11244
11245     /**
11246      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11247      *   Will fail silently if the setUrl method has not been called.
11248      *   This does not activate the panel, just updates its content.
11249      */
11250     refresh : function(){
11251         if(this.refreshDelegate){
11252            this.loaded = false;
11253            this.refreshDelegate();
11254         }
11255     },
11256
11257     /** @private */
11258     _setLoaded : function(){
11259         this.loaded = true;
11260     },
11261
11262     /** @private */
11263     closeClick : function(e){
11264         var o = {};
11265         e.stopEvent();
11266         this.fireEvent("beforeclose", this, o);
11267         if(o.cancel !== true){
11268             this.tabPanel.removeTab(this.id);
11269         }
11270     },
11271     /**
11272      * The text displayed in the tooltip for the close icon.
11273      * @type String
11274      */
11275     closeText : "Close this tab"
11276 });
11277
11278 /** @private */
11279 Roo.TabPanel.prototype.createStrip = function(container){
11280     var strip = document.createElement("div");
11281     strip.className = "x-tabs-wrap";
11282     container.appendChild(strip);
11283     return strip;
11284 };
11285 /** @private */
11286 Roo.TabPanel.prototype.createStripList = function(strip){
11287     // div wrapper for retard IE
11288     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>';
11289     return strip.firstChild.firstChild.firstChild.firstChild;
11290 };
11291 /** @private */
11292 Roo.TabPanel.prototype.createBody = function(container){
11293     var body = document.createElement("div");
11294     Roo.id(body, "tab-body");
11295     Roo.fly(body).addClass("x-tabs-body");
11296     container.appendChild(body);
11297     return body;
11298 };
11299 /** @private */
11300 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11301     var body = Roo.getDom(id);
11302     if(!body){
11303         body = document.createElement("div");
11304         body.id = id;
11305     }
11306     Roo.fly(body).addClass("x-tabs-item-body");
11307     bodyEl.insertBefore(body, bodyEl.firstChild);
11308     return body;
11309 };
11310 /** @private */
11311 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11312     var td = document.createElement("td");
11313     stripEl.appendChild(td);
11314     if(closable){
11315         td.className = "x-tabs-closable";
11316         if(!this.closeTpl){
11317             this.closeTpl = new Roo.Template(
11318                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11319                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11320                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11321             );
11322         }
11323         var el = this.closeTpl.overwrite(td, {"text": text});
11324         var close = el.getElementsByTagName("div")[0];
11325         var inner = el.getElementsByTagName("em")[0];
11326         return {"el": el, "close": close, "inner": inner};
11327     } else {
11328         if(!this.tabTpl){
11329             this.tabTpl = new Roo.Template(
11330                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11331                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11332             );
11333         }
11334         var el = this.tabTpl.overwrite(td, {"text": text});
11335         var inner = el.getElementsByTagName("em")[0];
11336         return {"el": el, "inner": inner};
11337     }
11338 };/*
11339  * Based on:
11340  * Ext JS Library 1.1.1
11341  * Copyright(c) 2006-2007, Ext JS, LLC.
11342  *
11343  * Originally Released Under LGPL - original licence link has changed is not relivant.
11344  *
11345  * Fork - LGPL
11346  * <script type="text/javascript">
11347  */
11348
11349 /**
11350  * @class Roo.Button
11351  * @extends Roo.util.Observable
11352  * Simple Button class
11353  * @cfg {String} text The button text
11354  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11355  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11356  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11357  * @cfg {Object} scope The scope of the handler
11358  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11359  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11360  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11361  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11362  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11363  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11364    applies if enableToggle = true)
11365  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11366  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11367   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11368  * @constructor
11369  * Create a new button
11370  * @param {Object} config The config object
11371  */
11372 Roo.Button = function(renderTo, config)
11373 {
11374     if (!config) {
11375         config = renderTo;
11376         renderTo = config.renderTo || false;
11377     }
11378     
11379     Roo.apply(this, config);
11380     this.addEvents({
11381         /**
11382              * @event click
11383              * Fires when this button is clicked
11384              * @param {Button} this
11385              * @param {EventObject} e The click event
11386              */
11387             "click" : true,
11388         /**
11389              * @event toggle
11390              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11391              * @param {Button} this
11392              * @param {Boolean} pressed
11393              */
11394             "toggle" : true,
11395         /**
11396              * @event mouseover
11397              * Fires when the mouse hovers over the button
11398              * @param {Button} this
11399              * @param {Event} e The event object
11400              */
11401         'mouseover' : true,
11402         /**
11403              * @event mouseout
11404              * Fires when the mouse exits the button
11405              * @param {Button} this
11406              * @param {Event} e The event object
11407              */
11408         'mouseout': true,
11409          /**
11410              * @event render
11411              * Fires when the button is rendered
11412              * @param {Button} this
11413              */
11414         'render': true
11415     });
11416     if(this.menu){
11417         this.menu = Roo.menu.MenuMgr.get(this.menu);
11418     }
11419     if(renderTo){
11420         this.render(renderTo);
11421     }
11422     
11423     Roo.util.Observable.call(this);
11424 };
11425
11426 Roo.extend(Roo.Button, Roo.util.Observable, {
11427     /**
11428      * 
11429      */
11430     
11431     /**
11432      * Read-only. True if this button is hidden
11433      * @type Boolean
11434      */
11435     hidden : false,
11436     /**
11437      * Read-only. True if this button is disabled
11438      * @type Boolean
11439      */
11440     disabled : false,
11441     /**
11442      * Read-only. True if this button is pressed (only if enableToggle = true)
11443      * @type Boolean
11444      */
11445     pressed : false,
11446
11447     /**
11448      * @cfg {Number} tabIndex 
11449      * The DOM tabIndex for this button (defaults to undefined)
11450      */
11451     tabIndex : undefined,
11452
11453     /**
11454      * @cfg {Boolean} enableToggle
11455      * True to enable pressed/not pressed toggling (defaults to false)
11456      */
11457     enableToggle: false,
11458     /**
11459      * @cfg {Mixed} menu
11460      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11461      */
11462     menu : undefined,
11463     /**
11464      * @cfg {String} menuAlign
11465      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11466      */
11467     menuAlign : "tl-bl?",
11468
11469     /**
11470      * @cfg {String} iconCls
11471      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11472      */
11473     iconCls : undefined,
11474     /**
11475      * @cfg {String} type
11476      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11477      */
11478     type : 'button',
11479
11480     // private
11481     menuClassTarget: 'tr',
11482
11483     /**
11484      * @cfg {String} clickEvent
11485      * The type of event to map to the button's event handler (defaults to 'click')
11486      */
11487     clickEvent : 'click',
11488
11489     /**
11490      * @cfg {Boolean} handleMouseEvents
11491      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11492      */
11493     handleMouseEvents : true,
11494
11495     /**
11496      * @cfg {String} tooltipType
11497      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11498      */
11499     tooltipType : 'qtip',
11500
11501     /**
11502      * @cfg {String} cls
11503      * A CSS class to apply to the button's main element.
11504      */
11505     
11506     /**
11507      * @cfg {Roo.Template} template (Optional)
11508      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11509      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11510      * require code modifications if required elements (e.g. a button) aren't present.
11511      */
11512
11513     // private
11514     render : function(renderTo){
11515         var btn;
11516         if(this.hideParent){
11517             this.parentEl = Roo.get(renderTo);
11518         }
11519         if(!this.dhconfig){
11520             if(!this.template){
11521                 if(!Roo.Button.buttonTemplate){
11522                     // hideous table template
11523                     Roo.Button.buttonTemplate = new Roo.Template(
11524                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11525                         '<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>',
11526                         "</tr></tbody></table>");
11527                 }
11528                 this.template = Roo.Button.buttonTemplate;
11529             }
11530             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11531             var btnEl = btn.child("button:first");
11532             btnEl.on('focus', this.onFocus, this);
11533             btnEl.on('blur', this.onBlur, this);
11534             if(this.cls){
11535                 btn.addClass(this.cls);
11536             }
11537             if(this.icon){
11538                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11539             }
11540             if(this.iconCls){
11541                 btnEl.addClass(this.iconCls);
11542                 if(!this.cls){
11543                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11544                 }
11545             }
11546             if(this.tabIndex !== undefined){
11547                 btnEl.dom.tabIndex = this.tabIndex;
11548             }
11549             if(this.tooltip){
11550                 if(typeof this.tooltip == 'object'){
11551                     Roo.QuickTips.tips(Roo.apply({
11552                           target: btnEl.id
11553                     }, this.tooltip));
11554                 } else {
11555                     btnEl.dom[this.tooltipType] = this.tooltip;
11556                 }
11557             }
11558         }else{
11559             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11560         }
11561         this.el = btn;
11562         if(this.id){
11563             this.el.dom.id = this.el.id = this.id;
11564         }
11565         if(this.menu){
11566             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11567             this.menu.on("show", this.onMenuShow, this);
11568             this.menu.on("hide", this.onMenuHide, this);
11569         }
11570         btn.addClass("x-btn");
11571         if(Roo.isIE && !Roo.isIE7){
11572             this.autoWidth.defer(1, this);
11573         }else{
11574             this.autoWidth();
11575         }
11576         if(this.handleMouseEvents){
11577             btn.on("mouseover", this.onMouseOver, this);
11578             btn.on("mouseout", this.onMouseOut, this);
11579             btn.on("mousedown", this.onMouseDown, this);
11580         }
11581         btn.on(this.clickEvent, this.onClick, this);
11582         //btn.on("mouseup", this.onMouseUp, this);
11583         if(this.hidden){
11584             this.hide();
11585         }
11586         if(this.disabled){
11587             this.disable();
11588         }
11589         Roo.ButtonToggleMgr.register(this);
11590         if(this.pressed){
11591             this.el.addClass("x-btn-pressed");
11592         }
11593         if(this.repeat){
11594             var repeater = new Roo.util.ClickRepeater(btn,
11595                 typeof this.repeat == "object" ? this.repeat : {}
11596             );
11597             repeater.on("click", this.onClick,  this);
11598         }
11599         this.fireEvent('render', this);
11600         
11601     },
11602     /**
11603      * Returns the button's underlying element
11604      * @return {Roo.Element} The element
11605      */
11606     getEl : function(){
11607         return this.el;  
11608     },
11609     
11610     /**
11611      * Destroys this Button and removes any listeners.
11612      */
11613     destroy : function(){
11614         Roo.ButtonToggleMgr.unregister(this);
11615         this.el.removeAllListeners();
11616         this.purgeListeners();
11617         this.el.remove();
11618     },
11619
11620     // private
11621     autoWidth : function(){
11622         if(this.el){
11623             this.el.setWidth("auto");
11624             if(Roo.isIE7 && Roo.isStrict){
11625                 var ib = this.el.child('button');
11626                 if(ib && ib.getWidth() > 20){
11627                     ib.clip();
11628                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11629                 }
11630             }
11631             if(this.minWidth){
11632                 if(this.hidden){
11633                     this.el.beginMeasure();
11634                 }
11635                 if(this.el.getWidth() < this.minWidth){
11636                     this.el.setWidth(this.minWidth);
11637                 }
11638                 if(this.hidden){
11639                     this.el.endMeasure();
11640                 }
11641             }
11642         }
11643     },
11644
11645     /**
11646      * Assigns this button's click handler
11647      * @param {Function} handler The function to call when the button is clicked
11648      * @param {Object} scope (optional) Scope for the function passed in
11649      */
11650     setHandler : function(handler, scope){
11651         this.handler = handler;
11652         this.scope = scope;  
11653     },
11654     
11655     /**
11656      * Sets this button's text
11657      * @param {String} text The button text
11658      */
11659     setText : function(text){
11660         this.text = text;
11661         if(this.el){
11662             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11663         }
11664         this.autoWidth();
11665     },
11666     
11667     /**
11668      * Gets the text for this button
11669      * @return {String} The button text
11670      */
11671     getText : function(){
11672         return this.text;  
11673     },
11674     
11675     /**
11676      * Show this button
11677      */
11678     show: function(){
11679         this.hidden = false;
11680         if(this.el){
11681             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11682         }
11683     },
11684     
11685     /**
11686      * Hide this button
11687      */
11688     hide: function(){
11689         this.hidden = true;
11690         if(this.el){
11691             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11692         }
11693     },
11694     
11695     /**
11696      * Convenience function for boolean show/hide
11697      * @param {Boolean} visible True to show, false to hide
11698      */
11699     setVisible: function(visible){
11700         if(visible) {
11701             this.show();
11702         }else{
11703             this.hide();
11704         }
11705     },
11706     
11707     /**
11708      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11709      * @param {Boolean} state (optional) Force a particular state
11710      */
11711     toggle : function(state){
11712         state = state === undefined ? !this.pressed : state;
11713         if(state != this.pressed){
11714             if(state){
11715                 this.el.addClass("x-btn-pressed");
11716                 this.pressed = true;
11717                 this.fireEvent("toggle", this, true);
11718             }else{
11719                 this.el.removeClass("x-btn-pressed");
11720                 this.pressed = false;
11721                 this.fireEvent("toggle", this, false);
11722             }
11723             if(this.toggleHandler){
11724                 this.toggleHandler.call(this.scope || this, this, state);
11725             }
11726         }
11727     },
11728     
11729     /**
11730      * Focus the button
11731      */
11732     focus : function(){
11733         this.el.child('button:first').focus();
11734     },
11735     
11736     /**
11737      * Disable this button
11738      */
11739     disable : function(){
11740         if(this.el){
11741             this.el.addClass("x-btn-disabled");
11742         }
11743         this.disabled = true;
11744     },
11745     
11746     /**
11747      * Enable this button
11748      */
11749     enable : function(){
11750         if(this.el){
11751             this.el.removeClass("x-btn-disabled");
11752         }
11753         this.disabled = false;
11754     },
11755
11756     /**
11757      * Convenience function for boolean enable/disable
11758      * @param {Boolean} enabled True to enable, false to disable
11759      */
11760     setDisabled : function(v){
11761         this[v !== true ? "enable" : "disable"]();
11762     },
11763
11764     // private
11765     onClick : function(e){
11766         if(e){
11767             e.preventDefault();
11768         }
11769         if(e.button != 0){
11770             return;
11771         }
11772         if(!this.disabled){
11773             if(this.enableToggle){
11774                 this.toggle();
11775             }
11776             if(this.menu && !this.menu.isVisible()){
11777                 this.menu.show(this.el, this.menuAlign);
11778             }
11779             this.fireEvent("click", this, e);
11780             if(this.handler){
11781                 this.el.removeClass("x-btn-over");
11782                 this.handler.call(this.scope || this, this, e);
11783             }
11784         }
11785     },
11786     // private
11787     onMouseOver : function(e){
11788         if(!this.disabled){
11789             this.el.addClass("x-btn-over");
11790             this.fireEvent('mouseover', this, e);
11791         }
11792     },
11793     // private
11794     onMouseOut : function(e){
11795         if(!e.within(this.el,  true)){
11796             this.el.removeClass("x-btn-over");
11797             this.fireEvent('mouseout', this, e);
11798         }
11799     },
11800     // private
11801     onFocus : function(e){
11802         if(!this.disabled){
11803             this.el.addClass("x-btn-focus");
11804         }
11805     },
11806     // private
11807     onBlur : function(e){
11808         this.el.removeClass("x-btn-focus");
11809     },
11810     // private
11811     onMouseDown : function(e){
11812         if(!this.disabled && e.button == 0){
11813             this.el.addClass("x-btn-click");
11814             Roo.get(document).on('mouseup', this.onMouseUp, this);
11815         }
11816     },
11817     // private
11818     onMouseUp : function(e){
11819         if(e.button == 0){
11820             this.el.removeClass("x-btn-click");
11821             Roo.get(document).un('mouseup', this.onMouseUp, this);
11822         }
11823     },
11824     // private
11825     onMenuShow : function(e){
11826         this.el.addClass("x-btn-menu-active");
11827     },
11828     // private
11829     onMenuHide : function(e){
11830         this.el.removeClass("x-btn-menu-active");
11831     }   
11832 });
11833
11834 // Private utility class used by Button
11835 Roo.ButtonToggleMgr = function(){
11836    var groups = {};
11837    
11838    function toggleGroup(btn, state){
11839        if(state){
11840            var g = groups[btn.toggleGroup];
11841            for(var i = 0, l = g.length; i < l; i++){
11842                if(g[i] != btn){
11843                    g[i].toggle(false);
11844                }
11845            }
11846        }
11847    }
11848    
11849    return {
11850        register : function(btn){
11851            if(!btn.toggleGroup){
11852                return;
11853            }
11854            var g = groups[btn.toggleGroup];
11855            if(!g){
11856                g = groups[btn.toggleGroup] = [];
11857            }
11858            g.push(btn);
11859            btn.on("toggle", toggleGroup);
11860        },
11861        
11862        unregister : function(btn){
11863            if(!btn.toggleGroup){
11864                return;
11865            }
11866            var g = groups[btn.toggleGroup];
11867            if(g){
11868                g.remove(btn);
11869                btn.un("toggle", toggleGroup);
11870            }
11871        }
11872    };
11873 }();/*
11874  * Based on:
11875  * Ext JS Library 1.1.1
11876  * Copyright(c) 2006-2007, Ext JS, LLC.
11877  *
11878  * Originally Released Under LGPL - original licence link has changed is not relivant.
11879  *
11880  * Fork - LGPL
11881  * <script type="text/javascript">
11882  */
11883  
11884 /**
11885  * @class Roo.SplitButton
11886  * @extends Roo.Button
11887  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11888  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11889  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11890  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11891  * @cfg {String} arrowTooltip The title attribute of the arrow
11892  * @constructor
11893  * Create a new menu button
11894  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11895  * @param {Object} config The config object
11896  */
11897 Roo.SplitButton = function(renderTo, config){
11898     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11899     /**
11900      * @event arrowclick
11901      * Fires when this button's arrow is clicked
11902      * @param {SplitButton} this
11903      * @param {EventObject} e The click event
11904      */
11905     this.addEvents({"arrowclick":true});
11906 };
11907
11908 Roo.extend(Roo.SplitButton, Roo.Button, {
11909     render : function(renderTo){
11910         // this is one sweet looking template!
11911         var tpl = new Roo.Template(
11912             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11913             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11914             '<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>',
11915             "</tbody></table></td><td>",
11916             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11917             '<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>',
11918             "</tbody></table></td></tr></table>"
11919         );
11920         var btn = tpl.append(renderTo, [this.text, this.type], true);
11921         var btnEl = btn.child("button");
11922         if(this.cls){
11923             btn.addClass(this.cls);
11924         }
11925         if(this.icon){
11926             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11927         }
11928         if(this.iconCls){
11929             btnEl.addClass(this.iconCls);
11930             if(!this.cls){
11931                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11932             }
11933         }
11934         this.el = btn;
11935         if(this.handleMouseEvents){
11936             btn.on("mouseover", this.onMouseOver, this);
11937             btn.on("mouseout", this.onMouseOut, this);
11938             btn.on("mousedown", this.onMouseDown, this);
11939             btn.on("mouseup", this.onMouseUp, this);
11940         }
11941         btn.on(this.clickEvent, this.onClick, this);
11942         if(this.tooltip){
11943             if(typeof this.tooltip == 'object'){
11944                 Roo.QuickTips.tips(Roo.apply({
11945                       target: btnEl.id
11946                 }, this.tooltip));
11947             } else {
11948                 btnEl.dom[this.tooltipType] = this.tooltip;
11949             }
11950         }
11951         if(this.arrowTooltip){
11952             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11953         }
11954         if(this.hidden){
11955             this.hide();
11956         }
11957         if(this.disabled){
11958             this.disable();
11959         }
11960         if(this.pressed){
11961             this.el.addClass("x-btn-pressed");
11962         }
11963         if(Roo.isIE && !Roo.isIE7){
11964             this.autoWidth.defer(1, this);
11965         }else{
11966             this.autoWidth();
11967         }
11968         if(this.menu){
11969             this.menu.on("show", this.onMenuShow, this);
11970             this.menu.on("hide", this.onMenuHide, this);
11971         }
11972         this.fireEvent('render', this);
11973     },
11974
11975     // private
11976     autoWidth : function(){
11977         if(this.el){
11978             var tbl = this.el.child("table:first");
11979             var tbl2 = this.el.child("table:last");
11980             this.el.setWidth("auto");
11981             tbl.setWidth("auto");
11982             if(Roo.isIE7 && Roo.isStrict){
11983                 var ib = this.el.child('button:first');
11984                 if(ib && ib.getWidth() > 20){
11985                     ib.clip();
11986                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11987                 }
11988             }
11989             if(this.minWidth){
11990                 if(this.hidden){
11991                     this.el.beginMeasure();
11992                 }
11993                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11994                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11995                 }
11996                 if(this.hidden){
11997                     this.el.endMeasure();
11998                 }
11999             }
12000             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12001         } 
12002     },
12003     /**
12004      * Sets this button's click handler
12005      * @param {Function} handler The function to call when the button is clicked
12006      * @param {Object} scope (optional) Scope for the function passed above
12007      */
12008     setHandler : function(handler, scope){
12009         this.handler = handler;
12010         this.scope = scope;  
12011     },
12012     
12013     /**
12014      * Sets this button's arrow click handler
12015      * @param {Function} handler The function to call when the arrow is clicked
12016      * @param {Object} scope (optional) Scope for the function passed above
12017      */
12018     setArrowHandler : function(handler, scope){
12019         this.arrowHandler = handler;
12020         this.scope = scope;  
12021     },
12022     
12023     /**
12024      * Focus the button
12025      */
12026     focus : function(){
12027         if(this.el){
12028             this.el.child("button:first").focus();
12029         }
12030     },
12031
12032     // private
12033     onClick : function(e){
12034         e.preventDefault();
12035         if(!this.disabled){
12036             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12037                 if(this.menu && !this.menu.isVisible()){
12038                     this.menu.show(this.el, this.menuAlign);
12039                 }
12040                 this.fireEvent("arrowclick", this, e);
12041                 if(this.arrowHandler){
12042                     this.arrowHandler.call(this.scope || this, this, e);
12043                 }
12044             }else{
12045                 this.fireEvent("click", this, e);
12046                 if(this.handler){
12047                     this.handler.call(this.scope || this, this, e);
12048                 }
12049             }
12050         }
12051     },
12052     // private
12053     onMouseDown : function(e){
12054         if(!this.disabled){
12055             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12056         }
12057     },
12058     // private
12059     onMouseUp : function(e){
12060         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12061     }   
12062 });
12063
12064
12065 // backwards compat
12066 Roo.MenuButton = Roo.SplitButton;/*
12067  * Based on:
12068  * Ext JS Library 1.1.1
12069  * Copyright(c) 2006-2007, Ext JS, LLC.
12070  *
12071  * Originally Released Under LGPL - original licence link has changed is not relivant.
12072  *
12073  * Fork - LGPL
12074  * <script type="text/javascript">
12075  */
12076
12077 /**
12078  * @class Roo.Toolbar
12079  * Basic Toolbar class.
12080  * @constructor
12081  * Creates a new Toolbar
12082  * @param {Object} config The config object
12083  */ 
12084 Roo.Toolbar = function(container, buttons, config)
12085 {
12086     /// old consturctor format still supported..
12087     if(container instanceof Array){ // omit the container for later rendering
12088         buttons = container;
12089         config = buttons;
12090         container = null;
12091     }
12092     if (typeof(container) == 'object' && container.xtype) {
12093         config = container;
12094         container = config.container;
12095         buttons = config.buttons; // not really - use items!!
12096     }
12097     var xitems = [];
12098     if (config && config.items) {
12099         xitems = config.items;
12100         delete config.items;
12101     }
12102     Roo.apply(this, config);
12103     this.buttons = buttons;
12104     
12105     if(container){
12106         this.render(container);
12107     }
12108     Roo.each(xitems, function(b) {
12109         this.add(b);
12110     }, this);
12111     
12112 };
12113
12114 Roo.Toolbar.prototype = {
12115     /**
12116      * @cfg {Roo.data.Store} items
12117      * array of button configs or elements to add
12118      */
12119     
12120     /**
12121      * @cfg {String/HTMLElement/Element} container
12122      * The id or element that will contain the toolbar
12123      */
12124     // private
12125     render : function(ct){
12126         this.el = Roo.get(ct);
12127         if(this.cls){
12128             this.el.addClass(this.cls);
12129         }
12130         // using a table allows for vertical alignment
12131         // 100% width is needed by Safari...
12132         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12133         this.tr = this.el.child("tr", true);
12134         var autoId = 0;
12135         this.items = new Roo.util.MixedCollection(false, function(o){
12136             return o.id || ("item" + (++autoId));
12137         });
12138         if(this.buttons){
12139             this.add.apply(this, this.buttons);
12140             delete this.buttons;
12141         }
12142     },
12143
12144     /**
12145      * Adds element(s) to the toolbar -- this function takes a variable number of 
12146      * arguments of mixed type and adds them to the toolbar.
12147      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12148      * <ul>
12149      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12150      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12151      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12152      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12153      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12154      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12155      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12156      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12157      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12158      * </ul>
12159      * @param {Mixed} arg2
12160      * @param {Mixed} etc.
12161      */
12162     add : function(){
12163         var a = arguments, l = a.length;
12164         for(var i = 0; i < l; i++){
12165             this._add(a[i]);
12166         }
12167     },
12168     // private..
12169     _add : function(el) {
12170         
12171         if (el.xtype) {
12172             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12173         }
12174         
12175         if (el.applyTo){ // some kind of form field
12176             return this.addField(el);
12177         } 
12178         if (el.render){ // some kind of Toolbar.Item
12179             return this.addItem(el);
12180         }
12181         if (typeof el == "string"){ // string
12182             if(el == "separator" || el == "-"){
12183                 return this.addSeparator();
12184             }
12185             if (el == " "){
12186                 return this.addSpacer();
12187             }
12188             if(el == "->"){
12189                 return this.addFill();
12190             }
12191             return this.addText(el);
12192             
12193         }
12194         if(el.tagName){ // element
12195             return this.addElement(el);
12196         }
12197         if(typeof el == "object"){ // must be button config?
12198             return this.addButton(el);
12199         }
12200         // and now what?!?!
12201         return false;
12202         
12203     },
12204     
12205     /**
12206      * Add an Xtype element
12207      * @param {Object} xtype Xtype Object
12208      * @return {Object} created Object
12209      */
12210     addxtype : function(e){
12211         return this.add(e);  
12212     },
12213     
12214     /**
12215      * Returns the Element for this toolbar.
12216      * @return {Roo.Element}
12217      */
12218     getEl : function(){
12219         return this.el;  
12220     },
12221     
12222     /**
12223      * Adds a separator
12224      * @return {Roo.Toolbar.Item} The separator item
12225      */
12226     addSeparator : function(){
12227         return this.addItem(new Roo.Toolbar.Separator());
12228     },
12229
12230     /**
12231      * Adds a spacer element
12232      * @return {Roo.Toolbar.Spacer} The spacer item
12233      */
12234     addSpacer : function(){
12235         return this.addItem(new Roo.Toolbar.Spacer());
12236     },
12237
12238     /**
12239      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12240      * @return {Roo.Toolbar.Fill} The fill item
12241      */
12242     addFill : function(){
12243         return this.addItem(new Roo.Toolbar.Fill());
12244     },
12245
12246     /**
12247      * Adds any standard HTML element to the toolbar
12248      * @param {String/HTMLElement/Element} el The element or id of the element to add
12249      * @return {Roo.Toolbar.Item} The element's item
12250      */
12251     addElement : function(el){
12252         return this.addItem(new Roo.Toolbar.Item(el));
12253     },
12254     /**
12255      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12256      * @type Roo.util.MixedCollection  
12257      */
12258     items : false,
12259      
12260     /**
12261      * Adds any Toolbar.Item or subclass
12262      * @param {Roo.Toolbar.Item} item
12263      * @return {Roo.Toolbar.Item} The item
12264      */
12265     addItem : function(item){
12266         var td = this.nextBlock();
12267         item.render(td);
12268         this.items.add(item);
12269         return item;
12270     },
12271     
12272     /**
12273      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12274      * @param {Object/Array} config A button config or array of configs
12275      * @return {Roo.Toolbar.Button/Array}
12276      */
12277     addButton : function(config){
12278         if(config instanceof Array){
12279             var buttons = [];
12280             for(var i = 0, len = config.length; i < len; i++) {
12281                 buttons.push(this.addButton(config[i]));
12282             }
12283             return buttons;
12284         }
12285         var b = config;
12286         if(!(config instanceof Roo.Toolbar.Button)){
12287             b = config.split ?
12288                 new Roo.Toolbar.SplitButton(config) :
12289                 new Roo.Toolbar.Button(config);
12290         }
12291         var td = this.nextBlock();
12292         b.render(td);
12293         this.items.add(b);
12294         return b;
12295     },
12296     
12297     /**
12298      * Adds text to the toolbar
12299      * @param {String} text The text to add
12300      * @return {Roo.Toolbar.Item} The element's item
12301      */
12302     addText : function(text){
12303         return this.addItem(new Roo.Toolbar.TextItem(text));
12304     },
12305     
12306     /**
12307      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12308      * @param {Number} index The index where the item is to be inserted
12309      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12310      * @return {Roo.Toolbar.Button/Item}
12311      */
12312     insertButton : function(index, item){
12313         if(item instanceof Array){
12314             var buttons = [];
12315             for(var i = 0, len = item.length; i < len; i++) {
12316                buttons.push(this.insertButton(index + i, item[i]));
12317             }
12318             return buttons;
12319         }
12320         if (!(item instanceof Roo.Toolbar.Button)){
12321            item = new Roo.Toolbar.Button(item);
12322         }
12323         var td = document.createElement("td");
12324         this.tr.insertBefore(td, this.tr.childNodes[index]);
12325         item.render(td);
12326         this.items.insert(index, item);
12327         return item;
12328     },
12329     
12330     /**
12331      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12332      * @param {Object} config
12333      * @return {Roo.Toolbar.Item} The element's item
12334      */
12335     addDom : function(config, returnEl){
12336         var td = this.nextBlock();
12337         Roo.DomHelper.overwrite(td, config);
12338         var ti = new Roo.Toolbar.Item(td.firstChild);
12339         ti.render(td);
12340         this.items.add(ti);
12341         return ti;
12342     },
12343
12344     /**
12345      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12346      * @type Roo.util.MixedCollection  
12347      */
12348     fields : false,
12349     
12350     /**
12351      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12352      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12353      * @param {Roo.form.Field} field
12354      * @return {Roo.ToolbarItem}
12355      */
12356      
12357       
12358     addField : function(field) {
12359         if (!this.fields) {
12360             var autoId = 0;
12361             this.fields = new Roo.util.MixedCollection(false, function(o){
12362                 return o.id || ("item" + (++autoId));
12363             });
12364
12365         }
12366         
12367         var td = this.nextBlock();
12368         field.render(td);
12369         var ti = new Roo.Toolbar.Item(td.firstChild);
12370         ti.render(td);
12371         this.items.add(ti);
12372         this.fields.add(field);
12373         return ti;
12374     },
12375     /**
12376      * Hide the toolbar
12377      * @method hide
12378      */
12379      
12380       
12381     hide : function()
12382     {
12383         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12384         this.el.child('div').hide();
12385     },
12386     /**
12387      * Show the toolbar
12388      * @method show
12389      */
12390     show : function()
12391     {
12392         this.el.child('div').show();
12393     },
12394       
12395     // private
12396     nextBlock : function(){
12397         var td = document.createElement("td");
12398         this.tr.appendChild(td);
12399         return td;
12400     },
12401
12402     // private
12403     destroy : function(){
12404         if(this.items){ // rendered?
12405             Roo.destroy.apply(Roo, this.items.items);
12406         }
12407         if(this.fields){ // rendered?
12408             Roo.destroy.apply(Roo, this.fields.items);
12409         }
12410         Roo.Element.uncache(this.el, this.tr);
12411     }
12412 };
12413
12414 /**
12415  * @class Roo.Toolbar.Item
12416  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12417  * @constructor
12418  * Creates a new Item
12419  * @param {HTMLElement} el 
12420  */
12421 Roo.Toolbar.Item = function(el){
12422     this.el = Roo.getDom(el);
12423     this.id = Roo.id(this.el);
12424     this.hidden = false;
12425 };
12426
12427 Roo.Toolbar.Item.prototype = {
12428     
12429     /**
12430      * Get this item's HTML Element
12431      * @return {HTMLElement}
12432      */
12433     getEl : function(){
12434        return this.el;  
12435     },
12436
12437     // private
12438     render : function(td){
12439         this.td = td;
12440         td.appendChild(this.el);
12441     },
12442     
12443     /**
12444      * Removes and destroys this item.
12445      */
12446     destroy : function(){
12447         this.td.parentNode.removeChild(this.td);
12448     },
12449     
12450     /**
12451      * Shows this item.
12452      */
12453     show: function(){
12454         this.hidden = false;
12455         this.td.style.display = "";
12456     },
12457     
12458     /**
12459      * Hides this item.
12460      */
12461     hide: function(){
12462         this.hidden = true;
12463         this.td.style.display = "none";
12464     },
12465     
12466     /**
12467      * Convenience function for boolean show/hide.
12468      * @param {Boolean} visible true to show/false to hide
12469      */
12470     setVisible: function(visible){
12471         if(visible) {
12472             this.show();
12473         }else{
12474             this.hide();
12475         }
12476     },
12477     
12478     /**
12479      * Try to focus this item.
12480      */
12481     focus : function(){
12482         Roo.fly(this.el).focus();
12483     },
12484     
12485     /**
12486      * Disables this item.
12487      */
12488     disable : function(){
12489         Roo.fly(this.td).addClass("x-item-disabled");
12490         this.disabled = true;
12491         this.el.disabled = true;
12492     },
12493     
12494     /**
12495      * Enables this item.
12496      */
12497     enable : function(){
12498         Roo.fly(this.td).removeClass("x-item-disabled");
12499         this.disabled = false;
12500         this.el.disabled = false;
12501     }
12502 };
12503
12504
12505 /**
12506  * @class Roo.Toolbar.Separator
12507  * @extends Roo.Toolbar.Item
12508  * A simple toolbar separator class
12509  * @constructor
12510  * Creates a new Separator
12511  */
12512 Roo.Toolbar.Separator = function(){
12513     var s = document.createElement("span");
12514     s.className = "ytb-sep";
12515     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12516 };
12517 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12518     enable:Roo.emptyFn,
12519     disable:Roo.emptyFn,
12520     focus:Roo.emptyFn
12521 });
12522
12523 /**
12524  * @class Roo.Toolbar.Spacer
12525  * @extends Roo.Toolbar.Item
12526  * A simple element that adds extra horizontal space to a toolbar.
12527  * @constructor
12528  * Creates a new Spacer
12529  */
12530 Roo.Toolbar.Spacer = function(){
12531     var s = document.createElement("div");
12532     s.className = "ytb-spacer";
12533     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12534 };
12535 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12536     enable:Roo.emptyFn,
12537     disable:Roo.emptyFn,
12538     focus:Roo.emptyFn
12539 });
12540
12541 /**
12542  * @class Roo.Toolbar.Fill
12543  * @extends Roo.Toolbar.Spacer
12544  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12545  * @constructor
12546  * Creates a new Spacer
12547  */
12548 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12549     // private
12550     render : function(td){
12551         td.style.width = '100%';
12552         Roo.Toolbar.Fill.superclass.render.call(this, td);
12553     }
12554 });
12555
12556 /**
12557  * @class Roo.Toolbar.TextItem
12558  * @extends Roo.Toolbar.Item
12559  * A simple class that renders text directly into a toolbar.
12560  * @constructor
12561  * Creates a new TextItem
12562  * @param {String} text
12563  */
12564 Roo.Toolbar.TextItem = function(text){
12565     if (typeof(text) == 'object') {
12566         text = text.text;
12567     }
12568     var s = document.createElement("span");
12569     s.className = "ytb-text";
12570     s.innerHTML = text;
12571     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12572 };
12573 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12574     enable:Roo.emptyFn,
12575     disable:Roo.emptyFn,
12576     focus:Roo.emptyFn
12577 });
12578
12579 /**
12580  * @class Roo.Toolbar.Button
12581  * @extends Roo.Button
12582  * A button that renders into a toolbar.
12583  * @constructor
12584  * Creates a new Button
12585  * @param {Object} config A standard {@link Roo.Button} config object
12586  */
12587 Roo.Toolbar.Button = function(config){
12588     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12589 };
12590 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12591     render : function(td){
12592         this.td = td;
12593         Roo.Toolbar.Button.superclass.render.call(this, td);
12594     },
12595     
12596     /**
12597      * Removes and destroys this button
12598      */
12599     destroy : function(){
12600         Roo.Toolbar.Button.superclass.destroy.call(this);
12601         this.td.parentNode.removeChild(this.td);
12602     },
12603     
12604     /**
12605      * Shows this button
12606      */
12607     show: function(){
12608         this.hidden = false;
12609         this.td.style.display = "";
12610     },
12611     
12612     /**
12613      * Hides this button
12614      */
12615     hide: function(){
12616         this.hidden = true;
12617         this.td.style.display = "none";
12618     },
12619
12620     /**
12621      * Disables this item
12622      */
12623     disable : function(){
12624         Roo.fly(this.td).addClass("x-item-disabled");
12625         this.disabled = true;
12626     },
12627
12628     /**
12629      * Enables this item
12630      */
12631     enable : function(){
12632         Roo.fly(this.td).removeClass("x-item-disabled");
12633         this.disabled = false;
12634     }
12635 });
12636 // backwards compat
12637 Roo.ToolbarButton = Roo.Toolbar.Button;
12638
12639 /**
12640  * @class Roo.Toolbar.SplitButton
12641  * @extends Roo.SplitButton
12642  * A menu button that renders into a toolbar.
12643  * @constructor
12644  * Creates a new SplitButton
12645  * @param {Object} config A standard {@link Roo.SplitButton} config object
12646  */
12647 Roo.Toolbar.SplitButton = function(config){
12648     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12649 };
12650 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12651     render : function(td){
12652         this.td = td;
12653         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12654     },
12655     
12656     /**
12657      * Removes and destroys this button
12658      */
12659     destroy : function(){
12660         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12661         this.td.parentNode.removeChild(this.td);
12662     },
12663     
12664     /**
12665      * Shows this button
12666      */
12667     show: function(){
12668         this.hidden = false;
12669         this.td.style.display = "";
12670     },
12671     
12672     /**
12673      * Hides this button
12674      */
12675     hide: function(){
12676         this.hidden = true;
12677         this.td.style.display = "none";
12678     }
12679 });
12680
12681 // backwards compat
12682 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12683  * Based on:
12684  * Ext JS Library 1.1.1
12685  * Copyright(c) 2006-2007, Ext JS, LLC.
12686  *
12687  * Originally Released Under LGPL - original licence link has changed is not relivant.
12688  *
12689  * Fork - LGPL
12690  * <script type="text/javascript">
12691  */
12692  
12693 /**
12694  * @class Roo.PagingToolbar
12695  * @extends Roo.Toolbar
12696  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12697  * @constructor
12698  * Create a new PagingToolbar
12699  * @param {Object} config The config object
12700  */
12701 Roo.PagingToolbar = function(el, ds, config)
12702 {
12703     // old args format still supported... - xtype is prefered..
12704     if (typeof(el) == 'object' && el.xtype) {
12705         // created from xtype...
12706         config = el;
12707         ds = el.dataSource;
12708         el = config.container;
12709     }
12710     
12711     
12712     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12713     this.ds = ds;
12714     this.cursor = 0;
12715     this.renderButtons(this.el);
12716     this.bind(ds);
12717 };
12718
12719 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12720     /**
12721      * @cfg {Roo.data.Store} dataSource
12722      * The underlying data store providing the paged data
12723      */
12724     /**
12725      * @cfg {String/HTMLElement/Element} container
12726      * container The id or element that will contain the toolbar
12727      */
12728     /**
12729      * @cfg {Boolean} displayInfo
12730      * True to display the displayMsg (defaults to false)
12731      */
12732     /**
12733      * @cfg {Number} pageSize
12734      * The number of records to display per page (defaults to 20)
12735      */
12736     pageSize: 20,
12737     /**
12738      * @cfg {String} displayMsg
12739      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12740      */
12741     displayMsg : 'Displaying {0} - {1} of {2}',
12742     /**
12743      * @cfg {String} emptyMsg
12744      * The message to display when no records are found (defaults to "No data to display")
12745      */
12746     emptyMsg : 'No data to display',
12747     /**
12748      * Customizable piece of the default paging text (defaults to "Page")
12749      * @type String
12750      */
12751     beforePageText : "Page",
12752     /**
12753      * Customizable piece of the default paging text (defaults to "of %0")
12754      * @type String
12755      */
12756     afterPageText : "of {0}",
12757     /**
12758      * Customizable piece of the default paging text (defaults to "First Page")
12759      * @type String
12760      */
12761     firstText : "First Page",
12762     /**
12763      * Customizable piece of the default paging text (defaults to "Previous Page")
12764      * @type String
12765      */
12766     prevText : "Previous Page",
12767     /**
12768      * Customizable piece of the default paging text (defaults to "Next Page")
12769      * @type String
12770      */
12771     nextText : "Next Page",
12772     /**
12773      * Customizable piece of the default paging text (defaults to "Last Page")
12774      * @type String
12775      */
12776     lastText : "Last Page",
12777     /**
12778      * Customizable piece of the default paging text (defaults to "Refresh")
12779      * @type String
12780      */
12781     refreshText : "Refresh",
12782
12783     // private
12784     renderButtons : function(el){
12785         Roo.PagingToolbar.superclass.render.call(this, el);
12786         this.first = this.addButton({
12787             tooltip: this.firstText,
12788             cls: "x-btn-icon x-grid-page-first",
12789             disabled: true,
12790             handler: this.onClick.createDelegate(this, ["first"])
12791         });
12792         this.prev = this.addButton({
12793             tooltip: this.prevText,
12794             cls: "x-btn-icon x-grid-page-prev",
12795             disabled: true,
12796             handler: this.onClick.createDelegate(this, ["prev"])
12797         });
12798         this.addSeparator();
12799         this.add(this.beforePageText);
12800         this.field = Roo.get(this.addDom({
12801            tag: "input",
12802            type: "text",
12803            size: "3",
12804            value: "1",
12805            cls: "x-grid-page-number"
12806         }).el);
12807         this.field.on("keydown", this.onPagingKeydown, this);
12808         this.field.on("focus", function(){this.dom.select();});
12809         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12810         this.field.setHeight(18);
12811         this.addSeparator();
12812         this.next = this.addButton({
12813             tooltip: this.nextText,
12814             cls: "x-btn-icon x-grid-page-next",
12815             disabled: true,
12816             handler: this.onClick.createDelegate(this, ["next"])
12817         });
12818         this.last = this.addButton({
12819             tooltip: this.lastText,
12820             cls: "x-btn-icon x-grid-page-last",
12821             disabled: true,
12822             handler: this.onClick.createDelegate(this, ["last"])
12823         });
12824         this.addSeparator();
12825         this.loading = this.addButton({
12826             tooltip: this.refreshText,
12827             cls: "x-btn-icon x-grid-loading",
12828             handler: this.onClick.createDelegate(this, ["refresh"])
12829         });
12830
12831         if(this.displayInfo){
12832             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12833         }
12834     },
12835
12836     // private
12837     updateInfo : function(){
12838         if(this.displayEl){
12839             var count = this.ds.getCount();
12840             var msg = count == 0 ?
12841                 this.emptyMsg :
12842                 String.format(
12843                     this.displayMsg,
12844                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12845                 );
12846             this.displayEl.update(msg);
12847         }
12848     },
12849
12850     // private
12851     onLoad : function(ds, r, o){
12852        this.cursor = o.params ? o.params.start : 0;
12853        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12854
12855        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12856        this.field.dom.value = ap;
12857        this.first.setDisabled(ap == 1);
12858        this.prev.setDisabled(ap == 1);
12859        this.next.setDisabled(ap == ps);
12860        this.last.setDisabled(ap == ps);
12861        this.loading.enable();
12862        this.updateInfo();
12863     },
12864
12865     // private
12866     getPageData : function(){
12867         var total = this.ds.getTotalCount();
12868         return {
12869             total : total,
12870             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12871             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12872         };
12873     },
12874
12875     // private
12876     onLoadError : function(){
12877         this.loading.enable();
12878     },
12879
12880     // private
12881     onPagingKeydown : function(e){
12882         var k = e.getKey();
12883         var d = this.getPageData();
12884         if(k == e.RETURN){
12885             var v = this.field.dom.value, pageNum;
12886             if(!v || isNaN(pageNum = parseInt(v, 10))){
12887                 this.field.dom.value = d.activePage;
12888                 return;
12889             }
12890             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12891             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12892             e.stopEvent();
12893         }
12894         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))
12895         {
12896           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12897           this.field.dom.value = pageNum;
12898           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12899           e.stopEvent();
12900         }
12901         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12902         {
12903           var v = this.field.dom.value, pageNum; 
12904           var increment = (e.shiftKey) ? 10 : 1;
12905           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12906             increment *= -1;
12907           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12908             this.field.dom.value = d.activePage;
12909             return;
12910           }
12911           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12912           {
12913             this.field.dom.value = parseInt(v, 10) + increment;
12914             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12915             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12916           }
12917           e.stopEvent();
12918         }
12919     },
12920
12921     // private
12922     beforeLoad : function(){
12923         if(this.loading){
12924             this.loading.disable();
12925         }
12926     },
12927
12928     // private
12929     onClick : function(which){
12930         var ds = this.ds;
12931         switch(which){
12932             case "first":
12933                 ds.load({params:{start: 0, limit: this.pageSize}});
12934             break;
12935             case "prev":
12936                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12937             break;
12938             case "next":
12939                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12940             break;
12941             case "last":
12942                 var total = ds.getTotalCount();
12943                 var extra = total % this.pageSize;
12944                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12945                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12946             break;
12947             case "refresh":
12948                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12949             break;
12950         }
12951     },
12952
12953     /**
12954      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12955      * @param {Roo.data.Store} store The data store to unbind
12956      */
12957     unbind : function(ds){
12958         ds.un("beforeload", this.beforeLoad, this);
12959         ds.un("load", this.onLoad, this);
12960         ds.un("loadexception", this.onLoadError, this);
12961         ds.un("remove", this.updateInfo, this);
12962         ds.un("add", this.updateInfo, this);
12963         this.ds = undefined;
12964     },
12965
12966     /**
12967      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12968      * @param {Roo.data.Store} store The data store to bind
12969      */
12970     bind : function(ds){
12971         ds.on("beforeload", this.beforeLoad, this);
12972         ds.on("load", this.onLoad, this);
12973         ds.on("loadexception", this.onLoadError, this);
12974         ds.on("remove", this.updateInfo, this);
12975         ds.on("add", this.updateInfo, this);
12976         this.ds = ds;
12977     }
12978 });/*
12979  * Based on:
12980  * Ext JS Library 1.1.1
12981  * Copyright(c) 2006-2007, Ext JS, LLC.
12982  *
12983  * Originally Released Under LGPL - original licence link has changed is not relivant.
12984  *
12985  * Fork - LGPL
12986  * <script type="text/javascript">
12987  */
12988
12989 /**
12990  * @class Roo.Resizable
12991  * @extends Roo.util.Observable
12992  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12993  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12994  * 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
12995  * the element will be wrapped for you automatically.</p>
12996  * <p>Here is the list of valid resize handles:</p>
12997  * <pre>
12998 Value   Description
12999 ------  -------------------
13000  'n'     north
13001  's'     south
13002  'e'     east
13003  'w'     west
13004  'nw'    northwest
13005  'sw'    southwest
13006  'se'    southeast
13007  'ne'    northeast
13008  'all'   all
13009 </pre>
13010  * <p>Here's an example showing the creation of a typical Resizable:</p>
13011  * <pre><code>
13012 var resizer = new Roo.Resizable("element-id", {
13013     handles: 'all',
13014     minWidth: 200,
13015     minHeight: 100,
13016     maxWidth: 500,
13017     maxHeight: 400,
13018     pinned: true
13019 });
13020 resizer.on("resize", myHandler);
13021 </code></pre>
13022  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13023  * resizer.east.setDisplayed(false);</p>
13024  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13025  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13026  * resize operation's new size (defaults to [0, 0])
13027  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13028  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13029  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13030  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13031  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13032  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13033  * @cfg {Number} width The width of the element in pixels (defaults to null)
13034  * @cfg {Number} height The height of the element in pixels (defaults to null)
13035  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13036  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13037  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13038  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13039  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13040  * in favor of the handles config option (defaults to false)
13041  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13042  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13043  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13044  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13045  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13046  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13047  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13048  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13049  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13050  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13051  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13052  * @constructor
13053  * Create a new resizable component
13054  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13055  * @param {Object} config configuration options
13056   */
13057 Roo.Resizable = function(el, config){
13058     this.el = Roo.get(el);
13059
13060     if(config && config.wrap){
13061         config.resizeChild = this.el;
13062         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13063         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13064         this.el.setStyle("overflow", "hidden");
13065         this.el.setPositioning(config.resizeChild.getPositioning());
13066         config.resizeChild.clearPositioning();
13067         if(!config.width || !config.height){
13068             var csize = config.resizeChild.getSize();
13069             this.el.setSize(csize.width, csize.height);
13070         }
13071         if(config.pinned && !config.adjustments){
13072             config.adjustments = "auto";
13073         }
13074     }
13075
13076     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13077     this.proxy.unselectable();
13078     this.proxy.enableDisplayMode('block');
13079
13080     Roo.apply(this, config);
13081
13082     if(this.pinned){
13083         this.disableTrackOver = true;
13084         this.el.addClass("x-resizable-pinned");
13085     }
13086     // if the element isn't positioned, make it relative
13087     var position = this.el.getStyle("position");
13088     if(position != "absolute" && position != "fixed"){
13089         this.el.setStyle("position", "relative");
13090     }
13091     if(!this.handles){ // no handles passed, must be legacy style
13092         this.handles = 's,e,se';
13093         if(this.multiDirectional){
13094             this.handles += ',n,w';
13095         }
13096     }
13097     if(this.handles == "all"){
13098         this.handles = "n s e w ne nw se sw";
13099     }
13100     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13101     var ps = Roo.Resizable.positions;
13102     for(var i = 0, len = hs.length; i < len; i++){
13103         if(hs[i] && ps[hs[i]]){
13104             var pos = ps[hs[i]];
13105             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13106         }
13107     }
13108     // legacy
13109     this.corner = this.southeast;
13110
13111     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
13112         this.updateBox = true;
13113     }
13114
13115     this.activeHandle = null;
13116
13117     if(this.resizeChild){
13118         if(typeof this.resizeChild == "boolean"){
13119             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13120         }else{
13121             this.resizeChild = Roo.get(this.resizeChild, true);
13122         }
13123     }
13124
13125     if(this.adjustments == "auto"){
13126         var rc = this.resizeChild;
13127         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13128         if(rc && (hw || hn)){
13129             rc.position("relative");
13130             rc.setLeft(hw ? hw.el.getWidth() : 0);
13131             rc.setTop(hn ? hn.el.getHeight() : 0);
13132         }
13133         this.adjustments = [
13134             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13135             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13136         ];
13137     }
13138
13139     if(this.draggable){
13140         this.dd = this.dynamic ?
13141             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13142         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13143     }
13144
13145     // public events
13146     this.addEvents({
13147         /**
13148          * @event beforeresize
13149          * Fired before resize is allowed. Set enabled to false to cancel resize.
13150          * @param {Roo.Resizable} this
13151          * @param {Roo.EventObject} e The mousedown event
13152          */
13153         "beforeresize" : true,
13154         /**
13155          * @event resize
13156          * Fired after a resize.
13157          * @param {Roo.Resizable} this
13158          * @param {Number} width The new width
13159          * @param {Number} height The new height
13160          * @param {Roo.EventObject} e The mouseup event
13161          */
13162         "resize" : true
13163     });
13164
13165     if(this.width !== null && this.height !== null){
13166         this.resizeTo(this.width, this.height);
13167     }else{
13168         this.updateChildSize();
13169     }
13170     if(Roo.isIE){
13171         this.el.dom.style.zoom = 1;
13172     }
13173     Roo.Resizable.superclass.constructor.call(this);
13174 };
13175
13176 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13177         resizeChild : false,
13178         adjustments : [0, 0],
13179         minWidth : 5,
13180         minHeight : 5,
13181         maxWidth : 10000,
13182         maxHeight : 10000,
13183         enabled : true,
13184         animate : false,
13185         duration : .35,
13186         dynamic : false,
13187         handles : false,
13188         multiDirectional : false,
13189         disableTrackOver : false,
13190         easing : 'easeOutStrong',
13191         widthIncrement : 0,
13192         heightIncrement : 0,
13193         pinned : false,
13194         width : null,
13195         height : null,
13196         preserveRatio : false,
13197         transparent: false,
13198         minX: 0,
13199         minY: 0,
13200         draggable: false,
13201
13202         /**
13203          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13204          */
13205         constrainTo: undefined,
13206         /**
13207          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13208          */
13209         resizeRegion: undefined,
13210
13211
13212     /**
13213      * Perform a manual resize
13214      * @param {Number} width
13215      * @param {Number} height
13216      */
13217     resizeTo : function(width, height){
13218         this.el.setSize(width, height);
13219         this.updateChildSize();
13220         this.fireEvent("resize", this, width, height, null);
13221     },
13222
13223     // private
13224     startSizing : function(e, handle){
13225         this.fireEvent("beforeresize", this, e);
13226         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13227
13228             if(!this.overlay){
13229                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13230                 this.overlay.unselectable();
13231                 this.overlay.enableDisplayMode("block");
13232                 this.overlay.on("mousemove", this.onMouseMove, this);
13233                 this.overlay.on("mouseup", this.onMouseUp, this);
13234             }
13235             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13236
13237             this.resizing = true;
13238             this.startBox = this.el.getBox();
13239             this.startPoint = e.getXY();
13240             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13241                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13242
13243             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13244             this.overlay.show();
13245
13246             if(this.constrainTo) {
13247                 var ct = Roo.get(this.constrainTo);
13248                 this.resizeRegion = ct.getRegion().adjust(
13249                     ct.getFrameWidth('t'),
13250                     ct.getFrameWidth('l'),
13251                     -ct.getFrameWidth('b'),
13252                     -ct.getFrameWidth('r')
13253                 );
13254             }
13255
13256             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13257             this.proxy.show();
13258             this.proxy.setBox(this.startBox);
13259             if(!this.dynamic){
13260                 this.proxy.setStyle('visibility', 'visible');
13261             }
13262         }
13263     },
13264
13265     // private
13266     onMouseDown : function(handle, e){
13267         if(this.enabled){
13268             e.stopEvent();
13269             this.activeHandle = handle;
13270             this.startSizing(e, handle);
13271         }
13272     },
13273
13274     // private
13275     onMouseUp : function(e){
13276         var size = this.resizeElement();
13277         this.resizing = false;
13278         this.handleOut();
13279         this.overlay.hide();
13280         this.proxy.hide();
13281         this.fireEvent("resize", this, size.width, size.height, e);
13282     },
13283
13284     // private
13285     updateChildSize : function(){
13286         if(this.resizeChild){
13287             var el = this.el;
13288             var child = this.resizeChild;
13289             var adj = this.adjustments;
13290             if(el.dom.offsetWidth){
13291                 var b = el.getSize(true);
13292                 child.setSize(b.width+adj[0], b.height+adj[1]);
13293             }
13294             // Second call here for IE
13295             // The first call enables instant resizing and
13296             // the second call corrects scroll bars if they
13297             // exist
13298             if(Roo.isIE){
13299                 setTimeout(function(){
13300                     if(el.dom.offsetWidth){
13301                         var b = el.getSize(true);
13302                         child.setSize(b.width+adj[0], b.height+adj[1]);
13303                     }
13304                 }, 10);
13305             }
13306         }
13307     },
13308
13309     // private
13310     snap : function(value, inc, min){
13311         if(!inc || !value) return value;
13312         var newValue = value;
13313         var m = value % inc;
13314         if(m > 0){
13315             if(m > (inc/2)){
13316                 newValue = value + (inc-m);
13317             }else{
13318                 newValue = value - m;
13319             }
13320         }
13321         return Math.max(min, newValue);
13322     },
13323
13324     // private
13325     resizeElement : function(){
13326         var box = this.proxy.getBox();
13327         if(this.updateBox){
13328             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13329         }else{
13330             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13331         }
13332         this.updateChildSize();
13333         if(!this.dynamic){
13334             this.proxy.hide();
13335         }
13336         return box;
13337     },
13338
13339     // private
13340     constrain : function(v, diff, m, mx){
13341         if(v - diff < m){
13342             diff = v - m;
13343         }else if(v - diff > mx){
13344             diff = mx - v;
13345         }
13346         return diff;
13347     },
13348
13349     // private
13350     onMouseMove : function(e){
13351         if(this.enabled){
13352             try{// try catch so if something goes wrong the user doesn't get hung
13353
13354             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13355                 return;
13356             }
13357
13358             //var curXY = this.startPoint;
13359             var curSize = this.curSize || this.startBox;
13360             var x = this.startBox.x, y = this.startBox.y;
13361             var ox = x, oy = y;
13362             var w = curSize.width, h = curSize.height;
13363             var ow = w, oh = h;
13364             var mw = this.minWidth, mh = this.minHeight;
13365             var mxw = this.maxWidth, mxh = this.maxHeight;
13366             var wi = this.widthIncrement;
13367             var hi = this.heightIncrement;
13368
13369             var eventXY = e.getXY();
13370             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13371             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13372
13373             var pos = this.activeHandle.position;
13374
13375             switch(pos){
13376                 case "east":
13377                     w += diffX;
13378                     w = Math.min(Math.max(mw, w), mxw);
13379                     break;
13380                 case "south":
13381                     h += diffY;
13382                     h = Math.min(Math.max(mh, h), mxh);
13383                     break;
13384                 case "southeast":
13385                     w += diffX;
13386                     h += diffY;
13387                     w = Math.min(Math.max(mw, w), mxw);
13388                     h = Math.min(Math.max(mh, h), mxh);
13389                     break;
13390                 case "north":
13391                     diffY = this.constrain(h, diffY, mh, mxh);
13392                     y += diffY;
13393                     h -= diffY;
13394                     break;
13395                 case "west":
13396                     diffX = this.constrain(w, diffX, mw, mxw);
13397                     x += diffX;
13398                     w -= diffX;
13399                     break;
13400                 case "northeast":
13401                     w += diffX;
13402                     w = Math.min(Math.max(mw, w), mxw);
13403                     diffY = this.constrain(h, diffY, mh, mxh);
13404                     y += diffY;
13405                     h -= diffY;
13406                     break;
13407                 case "northwest":
13408                     diffX = this.constrain(w, diffX, mw, mxw);
13409                     diffY = this.constrain(h, diffY, mh, mxh);
13410                     y += diffY;
13411                     h -= diffY;
13412                     x += diffX;
13413                     w -= diffX;
13414                     break;
13415                case "southwest":
13416                     diffX = this.constrain(w, diffX, mw, mxw);
13417                     h += diffY;
13418                     h = Math.min(Math.max(mh, h), mxh);
13419                     x += diffX;
13420                     w -= diffX;
13421                     break;
13422             }
13423
13424             var sw = this.snap(w, wi, mw);
13425             var sh = this.snap(h, hi, mh);
13426             if(sw != w || sh != h){
13427                 switch(pos){
13428                     case "northeast":
13429                         y -= sh - h;
13430                     break;
13431                     case "north":
13432                         y -= sh - h;
13433                         break;
13434                     case "southwest":
13435                         x -= sw - w;
13436                     break;
13437                     case "west":
13438                         x -= sw - w;
13439                         break;
13440                     case "northwest":
13441                         x -= sw - w;
13442                         y -= sh - h;
13443                     break;
13444                 }
13445                 w = sw;
13446                 h = sh;
13447             }
13448
13449             if(this.preserveRatio){
13450                 switch(pos){
13451                     case "southeast":
13452                     case "east":
13453                         h = oh * (w/ow);
13454                         h = Math.min(Math.max(mh, h), mxh);
13455                         w = ow * (h/oh);
13456                        break;
13457                     case "south":
13458                         w = ow * (h/oh);
13459                         w = Math.min(Math.max(mw, w), mxw);
13460                         h = oh * (w/ow);
13461                         break;
13462                     case "northeast":
13463                         w = ow * (h/oh);
13464                         w = Math.min(Math.max(mw, w), mxw);
13465                         h = oh * (w/ow);
13466                     break;
13467                     case "north":
13468                         var tw = w;
13469                         w = ow * (h/oh);
13470                         w = Math.min(Math.max(mw, w), mxw);
13471                         h = oh * (w/ow);
13472                         x += (tw - w) / 2;
13473                         break;
13474                     case "southwest":
13475                         h = oh * (w/ow);
13476                         h = Math.min(Math.max(mh, h), mxh);
13477                         var tw = w;
13478                         w = ow * (h/oh);
13479                         x += tw - w;
13480                         break;
13481                     case "west":
13482                         var th = h;
13483                         h = oh * (w/ow);
13484                         h = Math.min(Math.max(mh, h), mxh);
13485                         y += (th - h) / 2;
13486                         var tw = w;
13487                         w = ow * (h/oh);
13488                         x += tw - w;
13489                        break;
13490                     case "northwest":
13491                         var tw = w;
13492                         var th = h;
13493                         h = oh * (w/ow);
13494                         h = Math.min(Math.max(mh, h), mxh);
13495                         w = ow * (h/oh);
13496                         y += th - h;
13497                          x += tw - w;
13498                        break;
13499
13500                 }
13501             }
13502             this.proxy.setBounds(x, y, w, h);
13503             if(this.dynamic){
13504                 this.resizeElement();
13505             }
13506             }catch(e){}
13507         }
13508     },
13509
13510     // private
13511     handleOver : function(){
13512         if(this.enabled){
13513             this.el.addClass("x-resizable-over");
13514         }
13515     },
13516
13517     // private
13518     handleOut : function(){
13519         if(!this.resizing){
13520             this.el.removeClass("x-resizable-over");
13521         }
13522     },
13523
13524     /**
13525      * Returns the element this component is bound to.
13526      * @return {Roo.Element}
13527      */
13528     getEl : function(){
13529         return this.el;
13530     },
13531
13532     /**
13533      * Returns the resizeChild element (or null).
13534      * @return {Roo.Element}
13535      */
13536     getResizeChild : function(){
13537         return this.resizeChild;
13538     },
13539
13540     /**
13541      * Destroys this resizable. If the element was wrapped and
13542      * removeEl is not true then the element remains.
13543      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13544      */
13545     destroy : function(removeEl){
13546         this.proxy.remove();
13547         if(this.overlay){
13548             this.overlay.removeAllListeners();
13549             this.overlay.remove();
13550         }
13551         var ps = Roo.Resizable.positions;
13552         for(var k in ps){
13553             if(typeof ps[k] != "function" && this[ps[k]]){
13554                 var h = this[ps[k]];
13555                 h.el.removeAllListeners();
13556                 h.el.remove();
13557             }
13558         }
13559         if(removeEl){
13560             this.el.update("");
13561             this.el.remove();
13562         }
13563     }
13564 });
13565
13566 // private
13567 // hash to map config positions to true positions
13568 Roo.Resizable.positions = {
13569     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
13570 };
13571
13572 // private
13573 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13574     if(!this.tpl){
13575         // only initialize the template if resizable is used
13576         var tpl = Roo.DomHelper.createTemplate(
13577             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13578         );
13579         tpl.compile();
13580         Roo.Resizable.Handle.prototype.tpl = tpl;
13581     }
13582     this.position = pos;
13583     this.rz = rz;
13584     this.el = this.tpl.append(rz.el.dom, [this.position], true);
13585     this.el.unselectable();
13586     if(transparent){
13587         this.el.setOpacity(0);
13588     }
13589     this.el.on("mousedown", this.onMouseDown, this);
13590     if(!disableTrackOver){
13591         this.el.on("mouseover", this.onMouseOver, this);
13592         this.el.on("mouseout", this.onMouseOut, this);
13593     }
13594 };
13595
13596 // private
13597 Roo.Resizable.Handle.prototype = {
13598     afterResize : function(rz){
13599         // do nothing
13600     },
13601     // private
13602     onMouseDown : function(e){
13603         this.rz.onMouseDown(this, e);
13604     },
13605     // private
13606     onMouseOver : function(e){
13607         this.rz.handleOver(this, e);
13608     },
13609     // private
13610     onMouseOut : function(e){
13611         this.rz.handleOut(this, e);
13612     }
13613 };/*
13614  * Based on:
13615  * Ext JS Library 1.1.1
13616  * Copyright(c) 2006-2007, Ext JS, LLC.
13617  *
13618  * Originally Released Under LGPL - original licence link has changed is not relivant.
13619  *
13620  * Fork - LGPL
13621  * <script type="text/javascript">
13622  */
13623
13624 /**
13625  * @class Roo.Editor
13626  * @extends Roo.Component
13627  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13628  * @constructor
13629  * Create a new Editor
13630  * @param {Roo.form.Field} field The Field object (or descendant)
13631  * @param {Object} config The config object
13632  */
13633 Roo.Editor = function(field, config){
13634     Roo.Editor.superclass.constructor.call(this, config);
13635     this.field = field;
13636     this.addEvents({
13637         /**
13638              * @event beforestartedit
13639              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13640              * false from the handler of this event.
13641              * @param {Editor} this
13642              * @param {Roo.Element} boundEl The underlying element bound to this editor
13643              * @param {Mixed} value The field value being set
13644              */
13645         "beforestartedit" : true,
13646         /**
13647              * @event startedit
13648              * Fires when this editor is displayed
13649              * @param {Roo.Element} boundEl The underlying element bound to this editor
13650              * @param {Mixed} value The starting field value
13651              */
13652         "startedit" : true,
13653         /**
13654              * @event beforecomplete
13655              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13656              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13657              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13658              * event will not fire since no edit actually occurred.
13659              * @param {Editor} this
13660              * @param {Mixed} value The current field value
13661              * @param {Mixed} startValue The original field value
13662              */
13663         "beforecomplete" : true,
13664         /**
13665              * @event complete
13666              * Fires after editing is complete and any changed value has been written to the underlying field.
13667              * @param {Editor} this
13668              * @param {Mixed} value The current field value
13669              * @param {Mixed} startValue The original field value
13670              */
13671         "complete" : true,
13672         /**
13673          * @event specialkey
13674          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13675          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13676          * @param {Roo.form.Field} this
13677          * @param {Roo.EventObject} e The event object
13678          */
13679         "specialkey" : true
13680     });
13681 };
13682
13683 Roo.extend(Roo.Editor, Roo.Component, {
13684     /**
13685      * @cfg {Boolean/String} autosize
13686      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13687      * or "height" to adopt the height only (defaults to false)
13688      */
13689     /**
13690      * @cfg {Boolean} revertInvalid
13691      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13692      * validation fails (defaults to true)
13693      */
13694     /**
13695      * @cfg {Boolean} ignoreNoChange
13696      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13697      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13698      * will never be ignored.
13699      */
13700     /**
13701      * @cfg {Boolean} hideEl
13702      * False to keep the bound element visible while the editor is displayed (defaults to true)
13703      */
13704     /**
13705      * @cfg {Mixed} value
13706      * The data value of the underlying field (defaults to "")
13707      */
13708     value : "",
13709     /**
13710      * @cfg {String} alignment
13711      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13712      */
13713     alignment: "c-c?",
13714     /**
13715      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13716      * for bottom-right shadow (defaults to "frame")
13717      */
13718     shadow : "frame",
13719     /**
13720      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13721      */
13722     constrain : false,
13723     /**
13724      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13725      */
13726     completeOnEnter : false,
13727     /**
13728      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13729      */
13730     cancelOnEsc : false,
13731     /**
13732      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13733      */
13734     updateEl : false,
13735
13736     // private
13737     onRender : function(ct, position){
13738         this.el = new Roo.Layer({
13739             shadow: this.shadow,
13740             cls: "x-editor",
13741             parentEl : ct,
13742             shim : this.shim,
13743             shadowOffset:4,
13744             id: this.id,
13745             constrain: this.constrain
13746         });
13747         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13748         if(this.field.msgTarget != 'title'){
13749             this.field.msgTarget = 'qtip';
13750         }
13751         this.field.render(this.el);
13752         if(Roo.isGecko){
13753             this.field.el.dom.setAttribute('autocomplete', 'off');
13754         }
13755         this.field.on("specialkey", this.onSpecialKey, this);
13756         if(this.swallowKeys){
13757             this.field.el.swallowEvent(['keydown','keypress']);
13758         }
13759         this.field.show();
13760         this.field.on("blur", this.onBlur, this);
13761         if(this.field.grow){
13762             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13763         }
13764     },
13765
13766     onSpecialKey : function(field, e){
13767         if(this.completeOnEnter && e.getKey() == e.ENTER){
13768             e.stopEvent();
13769             this.completeEdit();
13770         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13771             this.cancelEdit();
13772         }else{
13773             this.fireEvent('specialkey', field, e);
13774         }
13775     },
13776
13777     /**
13778      * Starts the editing process and shows the editor.
13779      * @param {String/HTMLElement/Element} el The element to edit
13780      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13781       * to the innerHTML of el.
13782      */
13783     startEdit : function(el, value){
13784         if(this.editing){
13785             this.completeEdit();
13786         }
13787         this.boundEl = Roo.get(el);
13788         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13789         if(!this.rendered){
13790             this.render(this.parentEl || document.body);
13791         }
13792         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13793             return;
13794         }
13795         this.startValue = v;
13796         this.field.setValue(v);
13797         if(this.autoSize){
13798             var sz = this.boundEl.getSize();
13799             switch(this.autoSize){
13800                 case "width":
13801                 this.setSize(sz.width,  "");
13802                 break;
13803                 case "height":
13804                 this.setSize("",  sz.height);
13805                 break;
13806                 default:
13807                 this.setSize(sz.width,  sz.height);
13808             }
13809         }
13810         this.el.alignTo(this.boundEl, this.alignment);
13811         this.editing = true;
13812         if(Roo.QuickTips){
13813             Roo.QuickTips.disable();
13814         }
13815         this.show();
13816     },
13817
13818     /**
13819      * Sets the height and width of this editor.
13820      * @param {Number} width The new width
13821      * @param {Number} height The new height
13822      */
13823     setSize : function(w, h){
13824         this.field.setSize(w, h);
13825         if(this.el){
13826             this.el.sync();
13827         }
13828     },
13829
13830     /**
13831      * Realigns the editor to the bound field based on the current alignment config value.
13832      */
13833     realign : function(){
13834         this.el.alignTo(this.boundEl, this.alignment);
13835     },
13836
13837     /**
13838      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13839      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13840      */
13841     completeEdit : function(remainVisible){
13842         if(!this.editing){
13843             return;
13844         }
13845         var v = this.getValue();
13846         if(this.revertInvalid !== false && !this.field.isValid()){
13847             v = this.startValue;
13848             this.cancelEdit(true);
13849         }
13850         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13851             this.editing = false;
13852             this.hide();
13853             return;
13854         }
13855         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13856             this.editing = false;
13857             if(this.updateEl && this.boundEl){
13858                 this.boundEl.update(v);
13859             }
13860             if(remainVisible !== true){
13861                 this.hide();
13862             }
13863             this.fireEvent("complete", this, v, this.startValue);
13864         }
13865     },
13866
13867     // private
13868     onShow : function(){
13869         this.el.show();
13870         if(this.hideEl !== false){
13871             this.boundEl.hide();
13872         }
13873         this.field.show();
13874         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13875             this.fixIEFocus = true;
13876             this.deferredFocus.defer(50, this);
13877         }else{
13878             this.field.focus();
13879         }
13880         this.fireEvent("startedit", this.boundEl, this.startValue);
13881     },
13882
13883     deferredFocus : function(){
13884         if(this.editing){
13885             this.field.focus();
13886         }
13887     },
13888
13889     /**
13890      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13891      * reverted to the original starting value.
13892      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13893      * cancel (defaults to false)
13894      */
13895     cancelEdit : function(remainVisible){
13896         if(this.editing){
13897             this.setValue(this.startValue);
13898             if(remainVisible !== true){
13899                 this.hide();
13900             }
13901         }
13902     },
13903
13904     // private
13905     onBlur : function(){
13906         if(this.allowBlur !== true && this.editing){
13907             this.completeEdit();
13908         }
13909     },
13910
13911     // private
13912     onHide : function(){
13913         if(this.editing){
13914             this.completeEdit();
13915             return;
13916         }
13917         this.field.blur();
13918         if(this.field.collapse){
13919             this.field.collapse();
13920         }
13921         this.el.hide();
13922         if(this.hideEl !== false){
13923             this.boundEl.show();
13924         }
13925         if(Roo.QuickTips){
13926             Roo.QuickTips.enable();
13927         }
13928     },
13929
13930     /**
13931      * Sets the data value of the editor
13932      * @param {Mixed} value Any valid value supported by the underlying field
13933      */
13934     setValue : function(v){
13935         this.field.setValue(v);
13936     },
13937
13938     /**
13939      * Gets the data value of the editor
13940      * @return {Mixed} The data value
13941      */
13942     getValue : function(){
13943         return this.field.getValue();
13944     }
13945 });/*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955  
13956 /**
13957  * @class Roo.BasicDialog
13958  * @extends Roo.util.Observable
13959  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13960  * <pre><code>
13961 var dlg = new Roo.BasicDialog("my-dlg", {
13962     height: 200,
13963     width: 300,
13964     minHeight: 100,
13965     minWidth: 150,
13966     modal: true,
13967     proxyDrag: true,
13968     shadow: true
13969 });
13970 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13971 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13972 dlg.addButton('Cancel', dlg.hide, dlg);
13973 dlg.show();
13974 </code></pre>
13975   <b>A Dialog should always be a direct child of the body element.</b>
13976  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13977  * @cfg {String} title Default text to display in the title bar (defaults to null)
13978  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13979  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13980  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13981  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13982  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13983  * (defaults to null with no animation)
13984  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13985  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13986  * property for valid values (defaults to 'all')
13987  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13988  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13989  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13990  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13991  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13992  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13993  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13994  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13995  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13996  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13997  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13998  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13999  * draggable = true (defaults to false)
14000  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14001  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14002  * shadow (defaults to false)
14003  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14004  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14005  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14006  * @cfg {Array} buttons Array of buttons
14007  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14008  * @constructor
14009  * Create a new BasicDialog.
14010  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14011  * @param {Object} config Configuration options
14012  */
14013 Roo.BasicDialog = function(el, config){
14014     this.el = Roo.get(el);
14015     var dh = Roo.DomHelper;
14016     if(!this.el && config && config.autoCreate){
14017         if(typeof config.autoCreate == "object"){
14018             if(!config.autoCreate.id){
14019                 config.autoCreate.id = el;
14020             }
14021             this.el = dh.append(document.body,
14022                         config.autoCreate, true);
14023         }else{
14024             this.el = dh.append(document.body,
14025                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14026         }
14027     }
14028     el = this.el;
14029     el.setDisplayed(true);
14030     el.hide = this.hideAction;
14031     this.id = el.id;
14032     el.addClass("x-dlg");
14033
14034     Roo.apply(this, config);
14035
14036     this.proxy = el.createProxy("x-dlg-proxy");
14037     this.proxy.hide = this.hideAction;
14038     this.proxy.setOpacity(.5);
14039     this.proxy.hide();
14040
14041     if(config.width){
14042         el.setWidth(config.width);
14043     }
14044     if(config.height){
14045         el.setHeight(config.height);
14046     }
14047     this.size = el.getSize();
14048     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14049         this.xy = [config.x,config.y];
14050     }else{
14051         this.xy = el.getCenterXY(true);
14052     }
14053     /** The header element @type Roo.Element */
14054     this.header = el.child("> .x-dlg-hd");
14055     /** The body element @type Roo.Element */
14056     this.body = el.child("> .x-dlg-bd");
14057     /** The footer element @type Roo.Element */
14058     this.footer = el.child("> .x-dlg-ft");
14059
14060     if(!this.header){
14061         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14062     }
14063     if(!this.body){
14064         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14065     }
14066
14067     this.header.unselectable();
14068     if(this.title){
14069         this.header.update(this.title);
14070     }
14071     // this element allows the dialog to be focused for keyboard event
14072     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14073     this.focusEl.swallowEvent("click", true);
14074
14075     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14076
14077     // wrap the body and footer for special rendering
14078     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14079     if(this.footer){
14080         this.bwrap.dom.appendChild(this.footer.dom);
14081     }
14082
14083     this.bg = this.el.createChild({
14084         tag: "div", cls:"x-dlg-bg",
14085         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14086     });
14087     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14088
14089
14090     if(this.autoScroll !== false && !this.autoTabs){
14091         this.body.setStyle("overflow", "auto");
14092     }
14093
14094     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14095
14096     if(this.closable !== false){
14097         this.el.addClass("x-dlg-closable");
14098         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14099         this.close.on("click", this.closeClick, this);
14100         this.close.addClassOnOver("x-dlg-close-over");
14101     }
14102     if(this.collapsible !== false){
14103         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14104         this.collapseBtn.on("click", this.collapseClick, this);
14105         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14106         this.header.on("dblclick", this.collapseClick, this);
14107     }
14108     if(this.resizable !== false){
14109         this.el.addClass("x-dlg-resizable");
14110         this.resizer = new Roo.Resizable(el, {
14111             minWidth: this.minWidth || 80,
14112             minHeight:this.minHeight || 80,
14113             handles: this.resizeHandles || "all",
14114             pinned: true
14115         });
14116         this.resizer.on("beforeresize", this.beforeResize, this);
14117         this.resizer.on("resize", this.onResize, this);
14118     }
14119     if(this.draggable !== false){
14120         el.addClass("x-dlg-draggable");
14121         if (!this.proxyDrag) {
14122             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14123         }
14124         else {
14125             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14126         }
14127         dd.setHandleElId(this.header.id);
14128         dd.endDrag = this.endMove.createDelegate(this);
14129         dd.startDrag = this.startMove.createDelegate(this);
14130         dd.onDrag = this.onDrag.createDelegate(this);
14131         dd.scroll = false;
14132         this.dd = dd;
14133     }
14134     if(this.modal){
14135         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14136         this.mask.enableDisplayMode("block");
14137         this.mask.hide();
14138         this.el.addClass("x-dlg-modal");
14139     }
14140     if(this.shadow){
14141         this.shadow = new Roo.Shadow({
14142             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14143             offset : this.shadowOffset
14144         });
14145     }else{
14146         this.shadowOffset = 0;
14147     }
14148     if(Roo.useShims && this.shim !== false){
14149         this.shim = this.el.createShim();
14150         this.shim.hide = this.hideAction;
14151         this.shim.hide();
14152     }else{
14153         this.shim = false;
14154     }
14155     if(this.autoTabs){
14156         this.initTabs();
14157     }
14158     if (this.buttons) { 
14159         var bts= this.buttons;
14160         this.buttons = [];
14161         Roo.each(bts, function(b) {
14162             this.addButton(b);
14163         }, this);
14164     }
14165     
14166     
14167     this.addEvents({
14168         /**
14169          * @event keydown
14170          * Fires when a key is pressed
14171          * @param {Roo.BasicDialog} this
14172          * @param {Roo.EventObject} e
14173          */
14174         "keydown" : true,
14175         /**
14176          * @event move
14177          * Fires when this dialog is moved by the user.
14178          * @param {Roo.BasicDialog} this
14179          * @param {Number} x The new page X
14180          * @param {Number} y The new page Y
14181          */
14182         "move" : true,
14183         /**
14184          * @event resize
14185          * Fires when this dialog is resized by the user.
14186          * @param {Roo.BasicDialog} this
14187          * @param {Number} width The new width
14188          * @param {Number} height The new height
14189          */
14190         "resize" : true,
14191         /**
14192          * @event beforehide
14193          * Fires before this dialog is hidden.
14194          * @param {Roo.BasicDialog} this
14195          */
14196         "beforehide" : true,
14197         /**
14198          * @event hide
14199          * Fires when this dialog is hidden.
14200          * @param {Roo.BasicDialog} this
14201          */
14202         "hide" : true,
14203         /**
14204          * @event beforeshow
14205          * Fires before this dialog is shown.
14206          * @param {Roo.BasicDialog} this
14207          */
14208         "beforeshow" : true,
14209         /**
14210          * @event show
14211          * Fires when this dialog is shown.
14212          * @param {Roo.BasicDialog} this
14213          */
14214         "show" : true
14215     });
14216     el.on("keydown", this.onKeyDown, this);
14217     el.on("mousedown", this.toFront, this);
14218     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14219     this.el.hide();
14220     Roo.DialogManager.register(this);
14221     Roo.BasicDialog.superclass.constructor.call(this);
14222 };
14223
14224 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14225     shadowOffset: Roo.isIE ? 6 : 5,
14226     minHeight: 80,
14227     minWidth: 200,
14228     minButtonWidth: 75,
14229     defaultButton: null,
14230     buttonAlign: "right",
14231     tabTag: 'div',
14232     firstShow: true,
14233
14234     /**
14235      * Sets the dialog title text
14236      * @param {String} text The title text to display
14237      * @return {Roo.BasicDialog} this
14238      */
14239     setTitle : function(text){
14240         this.header.update(text);
14241         return this;
14242     },
14243
14244     // private
14245     closeClick : function(){
14246         this.hide();
14247     },
14248
14249     // private
14250     collapseClick : function(){
14251         this[this.collapsed ? "expand" : "collapse"]();
14252     },
14253
14254     /**
14255      * Collapses the dialog to its minimized state (only the title bar is visible).
14256      * Equivalent to the user clicking the collapse dialog button.
14257      */
14258     collapse : function(){
14259         if(!this.collapsed){
14260             this.collapsed = true;
14261             this.el.addClass("x-dlg-collapsed");
14262             this.restoreHeight = this.el.getHeight();
14263             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14264         }
14265     },
14266
14267     /**
14268      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14269      * clicking the expand dialog button.
14270      */
14271     expand : function(){
14272         if(this.collapsed){
14273             this.collapsed = false;
14274             this.el.removeClass("x-dlg-collapsed");
14275             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14276         }
14277     },
14278
14279     /**
14280      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14281      * @return {Roo.TabPanel} The tabs component
14282      */
14283     initTabs : function(){
14284         var tabs = this.getTabs();
14285         while(tabs.getTab(0)){
14286             tabs.removeTab(0);
14287         }
14288         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14289             var dom = el.dom;
14290             tabs.addTab(Roo.id(dom), dom.title);
14291             dom.title = "";
14292         });
14293         tabs.activate(0);
14294         return tabs;
14295     },
14296
14297     // private
14298     beforeResize : function(){
14299         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14300     },
14301
14302     // private
14303     onResize : function(){
14304         this.refreshSize();
14305         this.syncBodyHeight();
14306         this.adjustAssets();
14307         this.focus();
14308         this.fireEvent("resize", this, this.size.width, this.size.height);
14309     },
14310
14311     // private
14312     onKeyDown : function(e){
14313         if(this.isVisible()){
14314             this.fireEvent("keydown", this, e);
14315         }
14316     },
14317
14318     /**
14319      * Resizes the dialog.
14320      * @param {Number} width
14321      * @param {Number} height
14322      * @return {Roo.BasicDialog} this
14323      */
14324     resizeTo : function(width, height){
14325         this.el.setSize(width, height);
14326         this.size = {width: width, height: height};
14327         this.syncBodyHeight();
14328         if(this.fixedcenter){
14329             this.center();
14330         }
14331         if(this.isVisible()){
14332             this.constrainXY();
14333             this.adjustAssets();
14334         }
14335         this.fireEvent("resize", this, width, height);
14336         return this;
14337     },
14338
14339
14340     /**
14341      * Resizes the dialog to fit the specified content size.
14342      * @param {Number} width
14343      * @param {Number} height
14344      * @return {Roo.BasicDialog} this
14345      */
14346     setContentSize : function(w, h){
14347         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14348         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14349         //if(!this.el.isBorderBox()){
14350             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14351             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14352         //}
14353         if(this.tabs){
14354             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14355             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14356         }
14357         this.resizeTo(w, h);
14358         return this;
14359     },
14360
14361     /**
14362      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14363      * executed in response to a particular key being pressed while the dialog is active.
14364      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14365      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14366      * @param {Function} fn The function to call
14367      * @param {Object} scope (optional) The scope of the function
14368      * @return {Roo.BasicDialog} this
14369      */
14370     addKeyListener : function(key, fn, scope){
14371         var keyCode, shift, ctrl, alt;
14372         if(typeof key == "object" && !(key instanceof Array)){
14373             keyCode = key["key"];
14374             shift = key["shift"];
14375             ctrl = key["ctrl"];
14376             alt = key["alt"];
14377         }else{
14378             keyCode = key;
14379         }
14380         var handler = function(dlg, e){
14381             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14382                 var k = e.getKey();
14383                 if(keyCode instanceof Array){
14384                     for(var i = 0, len = keyCode.length; i < len; i++){
14385                         if(keyCode[i] == k){
14386                           fn.call(scope || window, dlg, k, e);
14387                           return;
14388                         }
14389                     }
14390                 }else{
14391                     if(k == keyCode){
14392                         fn.call(scope || window, dlg, k, e);
14393                     }
14394                 }
14395             }
14396         };
14397         this.on("keydown", handler);
14398         return this;
14399     },
14400
14401     /**
14402      * Returns the TabPanel component (creates it if it doesn't exist).
14403      * Note: If you wish to simply check for the existence of tabs without creating them,
14404      * check for a null 'tabs' property.
14405      * @return {Roo.TabPanel} The tabs component
14406      */
14407     getTabs : function(){
14408         if(!this.tabs){
14409             this.el.addClass("x-dlg-auto-tabs");
14410             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14411             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14412         }
14413         return this.tabs;
14414     },
14415
14416     /**
14417      * Adds a button to the footer section of the dialog.
14418      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14419      * object or a valid Roo.DomHelper element config
14420      * @param {Function} handler The function called when the button is clicked
14421      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14422      * @return {Roo.Button} The new button
14423      */
14424     addButton : function(config, handler, scope){
14425         var dh = Roo.DomHelper;
14426         if(!this.footer){
14427             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14428         }
14429         if(!this.btnContainer){
14430             var tb = this.footer.createChild({
14431
14432                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14433                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14434             }, null, true);
14435             this.btnContainer = tb.firstChild.firstChild.firstChild;
14436         }
14437         var bconfig = {
14438             handler: handler,
14439             scope: scope,
14440             minWidth: this.minButtonWidth,
14441             hideParent:true
14442         };
14443         if(typeof config == "string"){
14444             bconfig.text = config;
14445         }else{
14446             if(config.tag){
14447                 bconfig.dhconfig = config;
14448             }else{
14449                 Roo.apply(bconfig, config);
14450             }
14451         }
14452         var fc = false;
14453         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14454             bconfig.position = Math.max(0, bconfig.position);
14455             fc = this.btnContainer.childNodes[bconfig.position];
14456         }
14457          
14458         var btn = new Roo.Button(
14459             fc ? 
14460                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14461                 : this.btnContainer.appendChild(document.createElement("td")),
14462             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14463             bconfig
14464         );
14465         this.syncBodyHeight();
14466         if(!this.buttons){
14467             /**
14468              * Array of all the buttons that have been added to this dialog via addButton
14469              * @type Array
14470              */
14471             this.buttons = [];
14472         }
14473         this.buttons.push(btn);
14474         return btn;
14475     },
14476
14477     /**
14478      * Sets the default button to be focused when the dialog is displayed.
14479      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14480      * @return {Roo.BasicDialog} this
14481      */
14482     setDefaultButton : function(btn){
14483         this.defaultButton = btn;
14484         return this;
14485     },
14486
14487     // private
14488     getHeaderFooterHeight : function(safe){
14489         var height = 0;
14490         if(this.header){
14491            height += this.header.getHeight();
14492         }
14493         if(this.footer){
14494            var fm = this.footer.getMargins();
14495             height += (this.footer.getHeight()+fm.top+fm.bottom);
14496         }
14497         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14498         height += this.centerBg.getPadding("tb");
14499         return height;
14500     },
14501
14502     // private
14503     syncBodyHeight : function(){
14504         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14505         var height = this.size.height - this.getHeaderFooterHeight(false);
14506         bd.setHeight(height-bd.getMargins("tb"));
14507         var hh = this.header.getHeight();
14508         var h = this.size.height-hh;
14509         cb.setHeight(h);
14510         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14511         bw.setHeight(h-cb.getPadding("tb"));
14512         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14513         bd.setWidth(bw.getWidth(true));
14514         if(this.tabs){
14515             this.tabs.syncHeight();
14516             if(Roo.isIE){
14517                 this.tabs.el.repaint();
14518             }
14519         }
14520     },
14521
14522     /**
14523      * Restores the previous state of the dialog if Roo.state is configured.
14524      * @return {Roo.BasicDialog} this
14525      */
14526     restoreState : function(){
14527         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14528         if(box && box.width){
14529             this.xy = [box.x, box.y];
14530             this.resizeTo(box.width, box.height);
14531         }
14532         return this;
14533     },
14534
14535     // private
14536     beforeShow : function(){
14537         this.expand();
14538         if(this.fixedcenter){
14539             this.xy = this.el.getCenterXY(true);
14540         }
14541         if(this.modal){
14542             Roo.get(document.body).addClass("x-body-masked");
14543             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14544             this.mask.show();
14545         }
14546         this.constrainXY();
14547     },
14548
14549     // private
14550     animShow : function(){
14551         var b = Roo.get(this.animateTarget, true).getBox();
14552         this.proxy.setSize(b.width, b.height);
14553         this.proxy.setLocation(b.x, b.y);
14554         this.proxy.show();
14555         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14556                     true, .35, this.showEl.createDelegate(this));
14557     },
14558
14559     /**
14560      * Shows the dialog.
14561      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14562      * @return {Roo.BasicDialog} this
14563      */
14564     show : function(animateTarget){
14565         if (this.fireEvent("beforeshow", this) === false){
14566             return;
14567         }
14568         if(this.syncHeightBeforeShow){
14569             this.syncBodyHeight();
14570         }else if(this.firstShow){
14571             this.firstShow = false;
14572             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14573         }
14574         this.animateTarget = animateTarget || this.animateTarget;
14575         if(!this.el.isVisible()){
14576             this.beforeShow();
14577             if(this.animateTarget){
14578                 this.animShow();
14579             }else{
14580                 this.showEl();
14581             }
14582         }
14583         return this;
14584     },
14585
14586     // private
14587     showEl : function(){
14588         this.proxy.hide();
14589         this.el.setXY(this.xy);
14590         this.el.show();
14591         this.adjustAssets(true);
14592         this.toFront();
14593         this.focus();
14594         // IE peekaboo bug - fix found by Dave Fenwick
14595         if(Roo.isIE){
14596             this.el.repaint();
14597         }
14598         this.fireEvent("show", this);
14599     },
14600
14601     /**
14602      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14603      * dialog itself will receive focus.
14604      */
14605     focus : function(){
14606         if(this.defaultButton){
14607             this.defaultButton.focus();
14608         }else{
14609             this.focusEl.focus();
14610         }
14611     },
14612
14613     // private
14614     constrainXY : function(){
14615         if(this.constraintoviewport !== false){
14616             if(!this.viewSize){
14617                 if(this.container){
14618                     var s = this.container.getSize();
14619                     this.viewSize = [s.width, s.height];
14620                 }else{
14621                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14622                 }
14623             }
14624             var s = Roo.get(this.container||document).getScroll();
14625
14626             var x = this.xy[0], y = this.xy[1];
14627             var w = this.size.width, h = this.size.height;
14628             var vw = this.viewSize[0], vh = this.viewSize[1];
14629             // only move it if it needs it
14630             var moved = false;
14631             // first validate right/bottom
14632             if(x + w > vw+s.left){
14633                 x = vw - w;
14634                 moved = true;
14635             }
14636             if(y + h > vh+s.top){
14637                 y = vh - h;
14638                 moved = true;
14639             }
14640             // then make sure top/left isn't negative
14641             if(x < s.left){
14642                 x = s.left;
14643                 moved = true;
14644             }
14645             if(y < s.top){
14646                 y = s.top;
14647                 moved = true;
14648             }
14649             if(moved){
14650                 // cache xy
14651                 this.xy = [x, y];
14652                 if(this.isVisible()){
14653                     this.el.setLocation(x, y);
14654                     this.adjustAssets();
14655                 }
14656             }
14657         }
14658     },
14659
14660     // private
14661     onDrag : function(){
14662         if(!this.proxyDrag){
14663             this.xy = this.el.getXY();
14664             this.adjustAssets();
14665         }
14666     },
14667
14668     // private
14669     adjustAssets : function(doShow){
14670         var x = this.xy[0], y = this.xy[1];
14671         var w = this.size.width, h = this.size.height;
14672         if(doShow === true){
14673             if(this.shadow){
14674                 this.shadow.show(this.el);
14675             }
14676             if(this.shim){
14677                 this.shim.show();
14678             }
14679         }
14680         if(this.shadow && this.shadow.isVisible()){
14681             this.shadow.show(this.el);
14682         }
14683         if(this.shim && this.shim.isVisible()){
14684             this.shim.setBounds(x, y, w, h);
14685         }
14686     },
14687
14688     // private
14689     adjustViewport : function(w, h){
14690         if(!w || !h){
14691             w = Roo.lib.Dom.getViewWidth();
14692             h = Roo.lib.Dom.getViewHeight();
14693         }
14694         // cache the size
14695         this.viewSize = [w, h];
14696         if(this.modal && this.mask.isVisible()){
14697             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14698             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14699         }
14700         if(this.isVisible()){
14701             this.constrainXY();
14702         }
14703     },
14704
14705     /**
14706      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14707      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14708      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14709      */
14710     destroy : function(removeEl){
14711         if(this.isVisible()){
14712             this.animateTarget = null;
14713             this.hide();
14714         }
14715         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14716         if(this.tabs){
14717             this.tabs.destroy(removeEl);
14718         }
14719         Roo.destroy(
14720              this.shim,
14721              this.proxy,
14722              this.resizer,
14723              this.close,
14724              this.mask
14725         );
14726         if(this.dd){
14727             this.dd.unreg();
14728         }
14729         if(this.buttons){
14730            for(var i = 0, len = this.buttons.length; i < len; i++){
14731                this.buttons[i].destroy();
14732            }
14733         }
14734         this.el.removeAllListeners();
14735         if(removeEl === true){
14736             this.el.update("");
14737             this.el.remove();
14738         }
14739         Roo.DialogManager.unregister(this);
14740     },
14741
14742     // private
14743     startMove : function(){
14744         if(this.proxyDrag){
14745             this.proxy.show();
14746         }
14747         if(this.constraintoviewport !== false){
14748             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14749         }
14750     },
14751
14752     // private
14753     endMove : function(){
14754         if(!this.proxyDrag){
14755             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14756         }else{
14757             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14758             this.proxy.hide();
14759         }
14760         this.refreshSize();
14761         this.adjustAssets();
14762         this.focus();
14763         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14764     },
14765
14766     /**
14767      * Brings this dialog to the front of any other visible dialogs
14768      * @return {Roo.BasicDialog} this
14769      */
14770     toFront : function(){
14771         Roo.DialogManager.bringToFront(this);
14772         return this;
14773     },
14774
14775     /**
14776      * Sends this dialog to the back (under) of any other visible dialogs
14777      * @return {Roo.BasicDialog} this
14778      */
14779     toBack : function(){
14780         Roo.DialogManager.sendToBack(this);
14781         return this;
14782     },
14783
14784     /**
14785      * Centers this dialog in the viewport
14786      * @return {Roo.BasicDialog} this
14787      */
14788     center : function(){
14789         var xy = this.el.getCenterXY(true);
14790         this.moveTo(xy[0], xy[1]);
14791         return this;
14792     },
14793
14794     /**
14795      * Moves the dialog's top-left corner to the specified point
14796      * @param {Number} x
14797      * @param {Number} y
14798      * @return {Roo.BasicDialog} this
14799      */
14800     moveTo : function(x, y){
14801         this.xy = [x,y];
14802         if(this.isVisible()){
14803             this.el.setXY(this.xy);
14804             this.adjustAssets();
14805         }
14806         return this;
14807     },
14808
14809     /**
14810      * Aligns the dialog to the specified element
14811      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14812      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14813      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14814      * @return {Roo.BasicDialog} this
14815      */
14816     alignTo : function(element, position, offsets){
14817         this.xy = this.el.getAlignToXY(element, position, offsets);
14818         if(this.isVisible()){
14819             this.el.setXY(this.xy);
14820             this.adjustAssets();
14821         }
14822         return this;
14823     },
14824
14825     /**
14826      * Anchors an element to another element and realigns it when the window is resized.
14827      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14828      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14829      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14830      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14831      * is a number, it is used as the buffer delay (defaults to 50ms).
14832      * @return {Roo.BasicDialog} this
14833      */
14834     anchorTo : function(el, alignment, offsets, monitorScroll){
14835         var action = function(){
14836             this.alignTo(el, alignment, offsets);
14837         };
14838         Roo.EventManager.onWindowResize(action, this);
14839         var tm = typeof monitorScroll;
14840         if(tm != 'undefined'){
14841             Roo.EventManager.on(window, 'scroll', action, this,
14842                 {buffer: tm == 'number' ? monitorScroll : 50});
14843         }
14844         action.call(this);
14845         return this;
14846     },
14847
14848     /**
14849      * Returns true if the dialog is visible
14850      * @return {Boolean}
14851      */
14852     isVisible : function(){
14853         return this.el.isVisible();
14854     },
14855
14856     // private
14857     animHide : function(callback){
14858         var b = Roo.get(this.animateTarget).getBox();
14859         this.proxy.show();
14860         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14861         this.el.hide();
14862         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14863                     this.hideEl.createDelegate(this, [callback]));
14864     },
14865
14866     /**
14867      * Hides the dialog.
14868      * @param {Function} callback (optional) Function to call when the dialog is hidden
14869      * @return {Roo.BasicDialog} this
14870      */
14871     hide : function(callback){
14872         if (this.fireEvent("beforehide", this) === false){
14873             return;
14874         }
14875         if(this.shadow){
14876             this.shadow.hide();
14877         }
14878         if(this.shim) {
14879           this.shim.hide();
14880         }
14881         if(this.animateTarget){
14882            this.animHide(callback);
14883         }else{
14884             this.el.hide();
14885             this.hideEl(callback);
14886         }
14887         return this;
14888     },
14889
14890     // private
14891     hideEl : function(callback){
14892         this.proxy.hide();
14893         if(this.modal){
14894             this.mask.hide();
14895             Roo.get(document.body).removeClass("x-body-masked");
14896         }
14897         this.fireEvent("hide", this);
14898         if(typeof callback == "function"){
14899             callback();
14900         }
14901     },
14902
14903     // private
14904     hideAction : function(){
14905         this.setLeft("-10000px");
14906         this.setTop("-10000px");
14907         this.setStyle("visibility", "hidden");
14908     },
14909
14910     // private
14911     refreshSize : function(){
14912         this.size = this.el.getSize();
14913         this.xy = this.el.getXY();
14914         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14915     },
14916
14917     // private
14918     // z-index is managed by the DialogManager and may be overwritten at any time
14919     setZIndex : function(index){
14920         if(this.modal){
14921             this.mask.setStyle("z-index", index);
14922         }
14923         if(this.shim){
14924             this.shim.setStyle("z-index", ++index);
14925         }
14926         if(this.shadow){
14927             this.shadow.setZIndex(++index);
14928         }
14929         this.el.setStyle("z-index", ++index);
14930         if(this.proxy){
14931             this.proxy.setStyle("z-index", ++index);
14932         }
14933         if(this.resizer){
14934             this.resizer.proxy.setStyle("z-index", ++index);
14935         }
14936
14937         this.lastZIndex = index;
14938     },
14939
14940     /**
14941      * Returns the element for this dialog
14942      * @return {Roo.Element} The underlying dialog Element
14943      */
14944     getEl : function(){
14945         return this.el;
14946     }
14947 });
14948
14949 /**
14950  * @class Roo.DialogManager
14951  * Provides global access to BasicDialogs that have been created and
14952  * support for z-indexing (layering) multiple open dialogs.
14953  */
14954 Roo.DialogManager = function(){
14955     var list = {};
14956     var accessList = [];
14957     var front = null;
14958
14959     // private
14960     var sortDialogs = function(d1, d2){
14961         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14962     };
14963
14964     // private
14965     var orderDialogs = function(){
14966         accessList.sort(sortDialogs);
14967         var seed = Roo.DialogManager.zseed;
14968         for(var i = 0, len = accessList.length; i < len; i++){
14969             var dlg = accessList[i];
14970             if(dlg){
14971                 dlg.setZIndex(seed + (i*10));
14972             }
14973         }
14974     };
14975
14976     return {
14977         /**
14978          * The starting z-index for BasicDialogs (defaults to 9000)
14979          * @type Number The z-index value
14980          */
14981         zseed : 9000,
14982
14983         // private
14984         register : function(dlg){
14985             list[dlg.id] = dlg;
14986             accessList.push(dlg);
14987         },
14988
14989         // private
14990         unregister : function(dlg){
14991             delete list[dlg.id];
14992             var i=0;
14993             var len=0;
14994             if(!accessList.indexOf){
14995                 for(  i = 0, len = accessList.length; i < len; i++){
14996                     if(accessList[i] == dlg){
14997                         accessList.splice(i, 1);
14998                         return;
14999                     }
15000                 }
15001             }else{
15002                  i = accessList.indexOf(dlg);
15003                 if(i != -1){
15004                     accessList.splice(i, 1);
15005                 }
15006             }
15007         },
15008
15009         /**
15010          * Gets a registered dialog by id
15011          * @param {String/Object} id The id of the dialog or a dialog
15012          * @return {Roo.BasicDialog} this
15013          */
15014         get : function(id){
15015             return typeof id == "object" ? id : list[id];
15016         },
15017
15018         /**
15019          * Brings the specified dialog to the front
15020          * @param {String/Object} dlg The id of the dialog or a dialog
15021          * @return {Roo.BasicDialog} this
15022          */
15023         bringToFront : function(dlg){
15024             dlg = this.get(dlg);
15025             if(dlg != front){
15026                 front = dlg;
15027                 dlg._lastAccess = new Date().getTime();
15028                 orderDialogs();
15029             }
15030             return dlg;
15031         },
15032
15033         /**
15034          * Sends the specified dialog to the back
15035          * @param {String/Object} dlg The id of the dialog or a dialog
15036          * @return {Roo.BasicDialog} this
15037          */
15038         sendToBack : function(dlg){
15039             dlg = this.get(dlg);
15040             dlg._lastAccess = -(new Date().getTime());
15041             orderDialogs();
15042             return dlg;
15043         },
15044
15045         /**
15046          * Hides all dialogs
15047          */
15048         hideAll : function(){
15049             for(var id in list){
15050                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15051                     list[id].hide();
15052                 }
15053             }
15054         }
15055     };
15056 }();
15057
15058 /**
15059  * @class Roo.LayoutDialog
15060  * @extends Roo.BasicDialog
15061  * Dialog which provides adjustments for working with a layout in a Dialog.
15062  * Add your necessary layout config options to the dialog's config.<br>
15063  * Example usage (including a nested layout):
15064  * <pre><code>
15065 if(!dialog){
15066     dialog = new Roo.LayoutDialog("download-dlg", {
15067         modal: true,
15068         width:600,
15069         height:450,
15070         shadow:true,
15071         minWidth:500,
15072         minHeight:350,
15073         autoTabs:true,
15074         proxyDrag:true,
15075         // layout config merges with the dialog config
15076         center:{
15077             tabPosition: "top",
15078             alwaysShowTabs: true
15079         }
15080     });
15081     dialog.addKeyListener(27, dialog.hide, dialog);
15082     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15083     dialog.addButton("Build It!", this.getDownload, this);
15084
15085     // we can even add nested layouts
15086     var innerLayout = new Roo.BorderLayout("dl-inner", {
15087         east: {
15088             initialSize: 200,
15089             autoScroll:true,
15090             split:true
15091         },
15092         center: {
15093             autoScroll:true
15094         }
15095     });
15096     innerLayout.beginUpdate();
15097     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15098     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15099     innerLayout.endUpdate(true);
15100
15101     var layout = dialog.getLayout();
15102     layout.beginUpdate();
15103     layout.add("center", new Roo.ContentPanel("standard-panel",
15104                         {title: "Download the Source", fitToFrame:true}));
15105     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15106                {title: "Build your own roo.js"}));
15107     layout.getRegion("center").showPanel(sp);
15108     layout.endUpdate();
15109 }
15110 </code></pre>
15111     * @constructor
15112     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15113     * @param {Object} config configuration options
15114   */
15115 Roo.LayoutDialog = function(el, cfg){
15116     
15117     var config=  cfg;
15118     if (typeof(cfg) == 'undefined') {
15119         config = Roo.apply({}, el);
15120         el = Roo.get( document.documentElement || document.body).createChild();
15121         //config.autoCreate = true;
15122     }
15123     
15124     
15125     config.autoTabs = false;
15126     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15127     this.body.setStyle({overflow:"hidden", position:"relative"});
15128     this.layout = new Roo.BorderLayout(this.body.dom, config);
15129     this.layout.monitorWindowResize = false;
15130     this.el.addClass("x-dlg-auto-layout");
15131     // fix case when center region overwrites center function
15132     this.center = Roo.BasicDialog.prototype.center;
15133     this.on("show", this.layout.layout, this.layout, true);
15134     if (config.items) {
15135         var xitems = config.items;
15136         delete config.items;
15137         Roo.each(xitems, this.addxtype, this);
15138     }
15139     
15140     
15141 };
15142 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15143     /**
15144      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15145      * @deprecated
15146      */
15147     endUpdate : function(){
15148         this.layout.endUpdate();
15149     },
15150
15151     /**
15152      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15153      *  @deprecated
15154      */
15155     beginUpdate : function(){
15156         this.layout.beginUpdate();
15157     },
15158
15159     /**
15160      * Get the BorderLayout for this dialog
15161      * @return {Roo.BorderLayout}
15162      */
15163     getLayout : function(){
15164         return this.layout;
15165     },
15166
15167     showEl : function(){
15168         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15169         if(Roo.isIE7){
15170             this.layout.layout();
15171         }
15172     },
15173
15174     // private
15175     // Use the syncHeightBeforeShow config option to control this automatically
15176     syncBodyHeight : function(){
15177         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15178         if(this.layout){this.layout.layout();}
15179     },
15180     
15181       /**
15182      * Add an xtype element (actually adds to the layout.)
15183      * @return {Object} xdata xtype object data.
15184      */
15185     
15186     addxtype : function(c) {
15187         return this.layout.addxtype(c);
15188     }
15189 });/*
15190  * Based on:
15191  * Ext JS Library 1.1.1
15192  * Copyright(c) 2006-2007, Ext JS, LLC.
15193  *
15194  * Originally Released Under LGPL - original licence link has changed is not relivant.
15195  *
15196  * Fork - LGPL
15197  * <script type="text/javascript">
15198  */
15199  
15200 /**
15201  * @class Roo.MessageBox
15202  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15203  * Example usage:
15204  *<pre><code>
15205 // Basic alert:
15206 Roo.Msg.alert('Status', 'Changes saved successfully.');
15207
15208 // Prompt for user data:
15209 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15210     if (btn == 'ok'){
15211         // process text value...
15212     }
15213 });
15214
15215 // Show a dialog using config options:
15216 Roo.Msg.show({
15217    title:'Save Changes?',
15218    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15219    buttons: Roo.Msg.YESNOCANCEL,
15220    fn: processResult,
15221    animEl: 'elId'
15222 });
15223 </code></pre>
15224  * @singleton
15225  */
15226 Roo.MessageBox = function(){
15227     var dlg, opt, mask, waitTimer;
15228     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15229     var buttons, activeTextEl, bwidth;
15230
15231     // private
15232     var handleButton = function(button){
15233         dlg.hide();
15234         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15235     };
15236
15237     // private
15238     var handleHide = function(){
15239         if(opt && opt.cls){
15240             dlg.el.removeClass(opt.cls);
15241         }
15242         if(waitTimer){
15243             Roo.TaskMgr.stop(waitTimer);
15244             waitTimer = null;
15245         }
15246     };
15247
15248     // private
15249     var updateButtons = function(b){
15250         var width = 0;
15251         if(!b){
15252             buttons["ok"].hide();
15253             buttons["cancel"].hide();
15254             buttons["yes"].hide();
15255             buttons["no"].hide();
15256             dlg.footer.dom.style.display = 'none';
15257             return width;
15258         }
15259         dlg.footer.dom.style.display = '';
15260         for(var k in buttons){
15261             if(typeof buttons[k] != "function"){
15262                 if(b[k]){
15263                     buttons[k].show();
15264                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15265                     width += buttons[k].el.getWidth()+15;
15266                 }else{
15267                     buttons[k].hide();
15268                 }
15269             }
15270         }
15271         return width;
15272     };
15273
15274     // private
15275     var handleEsc = function(d, k, e){
15276         if(opt && opt.closable !== false){
15277             dlg.hide();
15278         }
15279         if(e){
15280             e.stopEvent();
15281         }
15282     };
15283
15284     return {
15285         /**
15286          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15287          * @return {Roo.BasicDialog} The BasicDialog element
15288          */
15289         getDialog : function(){
15290            if(!dlg){
15291                 dlg = new Roo.BasicDialog("x-msg-box", {
15292                     autoCreate : true,
15293                     shadow: true,
15294                     draggable: true,
15295                     resizable:false,
15296                     constraintoviewport:false,
15297                     fixedcenter:true,
15298                     collapsible : false,
15299                     shim:true,
15300                     modal: true,
15301                     width:400, height:100,
15302                     buttonAlign:"center",
15303                     closeClick : function(){
15304                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15305                             handleButton("no");
15306                         }else{
15307                             handleButton("cancel");
15308                         }
15309                     }
15310                 });
15311                 dlg.on("hide", handleHide);
15312                 mask = dlg.mask;
15313                 dlg.addKeyListener(27, handleEsc);
15314                 buttons = {};
15315                 var bt = this.buttonText;
15316                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15317                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15318                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15319                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15320                 bodyEl = dlg.body.createChild({
15321
15322                     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>'
15323                 });
15324                 msgEl = bodyEl.dom.firstChild;
15325                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15326                 textboxEl.enableDisplayMode();
15327                 textboxEl.addKeyListener([10,13], function(){
15328                     if(dlg.isVisible() && opt && opt.buttons){
15329                         if(opt.buttons.ok){
15330                             handleButton("ok");
15331                         }else if(opt.buttons.yes){
15332                             handleButton("yes");
15333                         }
15334                     }
15335                 });
15336                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15337                 textareaEl.enableDisplayMode();
15338                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15339                 progressEl.enableDisplayMode();
15340                 var pf = progressEl.dom.firstChild;
15341                 if (pf) {
15342                     pp = Roo.get(pf.firstChild);
15343                     pp.setHeight(pf.offsetHeight);
15344                 }
15345                 
15346             }
15347             return dlg;
15348         },
15349
15350         /**
15351          * Updates the message box body text
15352          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15353          * the XHTML-compliant non-breaking space character '&amp;#160;')
15354          * @return {Roo.MessageBox} This message box
15355          */
15356         updateText : function(text){
15357             if(!dlg.isVisible() && !opt.width){
15358                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15359             }
15360             msgEl.innerHTML = text || '&#160;';
15361             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15362                         Math.max(opt.minWidth || this.minWidth, bwidth));
15363             if(opt.prompt){
15364                 activeTextEl.setWidth(w);
15365             }
15366             if(dlg.isVisible()){
15367                 dlg.fixedcenter = false;
15368             }
15369             dlg.setContentSize(w, bodyEl.getHeight());
15370             if(dlg.isVisible()){
15371                 dlg.fixedcenter = true;
15372             }
15373             return this;
15374         },
15375
15376         /**
15377          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15378          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15379          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15380          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15381          * @return {Roo.MessageBox} This message box
15382          */
15383         updateProgress : function(value, text){
15384             if(text){
15385                 this.updateText(text);
15386             }
15387             if (pp) { // weird bug on my firefox - for some reason this is not defined
15388                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15389             }
15390             return this;
15391         },        
15392
15393         /**
15394          * Returns true if the message box is currently displayed
15395          * @return {Boolean} True if the message box is visible, else false
15396          */
15397         isVisible : function(){
15398             return dlg && dlg.isVisible();  
15399         },
15400
15401         /**
15402          * Hides the message box if it is displayed
15403          */
15404         hide : function(){
15405             if(this.isVisible()){
15406                 dlg.hide();
15407             }  
15408         },
15409
15410         /**
15411          * Displays a new message box, or reinitializes an existing message box, based on the config options
15412          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15413          * The following config object properties are supported:
15414          * <pre>
15415 Property    Type             Description
15416 ----------  ---------------  ------------------------------------------------------------------------------------
15417 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15418                                    closes (defaults to undefined)
15419 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15420                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15421 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15422                                    progress and wait dialogs will ignore this property and always hide the
15423                                    close button as they can only be closed programmatically.
15424 cls               String           A custom CSS class to apply to the message box element
15425 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15426                                    displayed (defaults to 75)
15427 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15428                                    function will be btn (the name of the button that was clicked, if applicable,
15429                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15430                                    Progress and wait dialogs will ignore this option since they do not respond to
15431                                    user actions and can only be closed programmatically, so any required function
15432                                    should be called by the same code after it closes the dialog.
15433 icon              String           A CSS class that provides a background image to be used as an icon for
15434                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15435 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15436 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15437 modal             Boolean          False to allow user interaction with the page while the message box is
15438                                    displayed (defaults to true)
15439 msg               String           A string that will replace the existing message box body text (defaults
15440                                    to the XHTML-compliant non-breaking space character '&#160;')
15441 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15442 progress          Boolean          True to display a progress bar (defaults to false)
15443 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15444 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15445 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15446 title             String           The title text
15447 value             String           The string value to set into the active textbox element if displayed
15448 wait              Boolean          True to display a progress bar (defaults to false)
15449 width             Number           The width of the dialog in pixels
15450 </pre>
15451          *
15452          * Example usage:
15453          * <pre><code>
15454 Roo.Msg.show({
15455    title: 'Address',
15456    msg: 'Please enter your address:',
15457    width: 300,
15458    buttons: Roo.MessageBox.OKCANCEL,
15459    multiline: true,
15460    fn: saveAddress,
15461    animEl: 'addAddressBtn'
15462 });
15463 </code></pre>
15464          * @param {Object} config Configuration options
15465          * @return {Roo.MessageBox} This message box
15466          */
15467         show : function(options){
15468             if(this.isVisible()){
15469                 this.hide();
15470             }
15471             var d = this.getDialog();
15472             opt = options;
15473             d.setTitle(opt.title || "&#160;");
15474             d.close.setDisplayed(opt.closable !== false);
15475             activeTextEl = textboxEl;
15476             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15477             if(opt.prompt){
15478                 if(opt.multiline){
15479                     textboxEl.hide();
15480                     textareaEl.show();
15481                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15482                         opt.multiline : this.defaultTextHeight);
15483                     activeTextEl = textareaEl;
15484                 }else{
15485                     textboxEl.show();
15486                     textareaEl.hide();
15487                 }
15488             }else{
15489                 textboxEl.hide();
15490                 textareaEl.hide();
15491             }
15492             progressEl.setDisplayed(opt.progress === true);
15493             this.updateProgress(0);
15494             activeTextEl.dom.value = opt.value || "";
15495             if(opt.prompt){
15496                 dlg.setDefaultButton(activeTextEl);
15497             }else{
15498                 var bs = opt.buttons;
15499                 var db = null;
15500                 if(bs && bs.ok){
15501                     db = buttons["ok"];
15502                 }else if(bs && bs.yes){
15503                     db = buttons["yes"];
15504                 }
15505                 dlg.setDefaultButton(db);
15506             }
15507             bwidth = updateButtons(opt.buttons);
15508             this.updateText(opt.msg);
15509             if(opt.cls){
15510                 d.el.addClass(opt.cls);
15511             }
15512             d.proxyDrag = opt.proxyDrag === true;
15513             d.modal = opt.modal !== false;
15514             d.mask = opt.modal !== false ? mask : false;
15515             if(!d.isVisible()){
15516                 // force it to the end of the z-index stack so it gets a cursor in FF
15517                 document.body.appendChild(dlg.el.dom);
15518                 d.animateTarget = null;
15519                 d.show(options.animEl);
15520             }
15521             return this;
15522         },
15523
15524         /**
15525          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15526          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15527          * and closing the message box when the process is complete.
15528          * @param {String} title The title bar text
15529          * @param {String} msg The message box body text
15530          * @return {Roo.MessageBox} This message box
15531          */
15532         progress : function(title, msg){
15533             this.show({
15534                 title : title,
15535                 msg : msg,
15536                 buttons: false,
15537                 progress:true,
15538                 closable:false,
15539                 minWidth: this.minProgressWidth,
15540                 modal : true
15541             });
15542             return this;
15543         },
15544
15545         /**
15546          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15547          * If a callback function is passed it will be called after the user clicks the button, and the
15548          * id of the button that was clicked will be passed as the only parameter to the callback
15549          * (could also be the top-right close button).
15550          * @param {String} title The title bar text
15551          * @param {String} msg The message box body text
15552          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15553          * @param {Object} scope (optional) The scope of the callback function
15554          * @return {Roo.MessageBox} This message box
15555          */
15556         alert : function(title, msg, fn, scope){
15557             this.show({
15558                 title : title,
15559                 msg : msg,
15560                 buttons: this.OK,
15561                 fn: fn,
15562                 scope : scope,
15563                 modal : true
15564             });
15565             return this;
15566         },
15567
15568         /**
15569          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15570          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15571          * You are responsible for closing the message box when the process is complete.
15572          * @param {String} msg The message box body text
15573          * @param {String} title (optional) The title bar text
15574          * @return {Roo.MessageBox} This message box
15575          */
15576         wait : function(msg, title){
15577             this.show({
15578                 title : title,
15579                 msg : msg,
15580                 buttons: false,
15581                 closable:false,
15582                 progress:true,
15583                 modal:true,
15584                 width:300,
15585                 wait:true
15586             });
15587             waitTimer = Roo.TaskMgr.start({
15588                 run: function(i){
15589                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15590                 },
15591                 interval: 1000
15592             });
15593             return this;
15594         },
15595
15596         /**
15597          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15598          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15599          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15600          * @param {String} title The title bar text
15601          * @param {String} msg The message box body text
15602          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15603          * @param {Object} scope (optional) The scope of the callback function
15604          * @return {Roo.MessageBox} This message box
15605          */
15606         confirm : function(title, msg, fn, scope){
15607             this.show({
15608                 title : title,
15609                 msg : msg,
15610                 buttons: this.YESNO,
15611                 fn: fn,
15612                 scope : scope,
15613                 modal : true
15614             });
15615             return this;
15616         },
15617
15618         /**
15619          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15620          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15621          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15622          * (could also be the top-right close button) and the text that was entered will be passed as the two
15623          * parameters to the callback.
15624          * @param {String} title The title bar text
15625          * @param {String} msg The message box body text
15626          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15627          * @param {Object} scope (optional) The scope of the callback function
15628          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15629          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15630          * @return {Roo.MessageBox} This message box
15631          */
15632         prompt : function(title, msg, fn, scope, multiline){
15633             this.show({
15634                 title : title,
15635                 msg : msg,
15636                 buttons: this.OKCANCEL,
15637                 fn: fn,
15638                 minWidth:250,
15639                 scope : scope,
15640                 prompt:true,
15641                 multiline: multiline,
15642                 modal : true
15643             });
15644             return this;
15645         },
15646
15647         /**
15648          * Button config that displays a single OK button
15649          * @type Object
15650          */
15651         OK : {ok:true},
15652         /**
15653          * Button config that displays Yes and No buttons
15654          * @type Object
15655          */
15656         YESNO : {yes:true, no:true},
15657         /**
15658          * Button config that displays OK and Cancel buttons
15659          * @type Object
15660          */
15661         OKCANCEL : {ok:true, cancel:true},
15662         /**
15663          * Button config that displays Yes, No and Cancel buttons
15664          * @type Object
15665          */
15666         YESNOCANCEL : {yes:true, no:true, cancel:true},
15667
15668         /**
15669          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15670          * @type Number
15671          */
15672         defaultTextHeight : 75,
15673         /**
15674          * The maximum width in pixels of the message box (defaults to 600)
15675          * @type Number
15676          */
15677         maxWidth : 600,
15678         /**
15679          * The minimum width in pixels of the message box (defaults to 100)
15680          * @type Number
15681          */
15682         minWidth : 100,
15683         /**
15684          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15685          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15686          * @type Number
15687          */
15688         minProgressWidth : 250,
15689         /**
15690          * An object containing the default button text strings that can be overriden for localized language support.
15691          * Supported properties are: ok, cancel, yes and no.
15692          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15693          * @type Object
15694          */
15695         buttonText : {
15696             ok : "OK",
15697             cancel : "Cancel",
15698             yes : "Yes",
15699             no : "No"
15700         }
15701     };
15702 }();
15703
15704 /**
15705  * Shorthand for {@link Roo.MessageBox}
15706  */
15707 Roo.Msg = Roo.MessageBox;/*
15708  * Based on:
15709  * Ext JS Library 1.1.1
15710  * Copyright(c) 2006-2007, Ext JS, LLC.
15711  *
15712  * Originally Released Under LGPL - original licence link has changed is not relivant.
15713  *
15714  * Fork - LGPL
15715  * <script type="text/javascript">
15716  */
15717 /**
15718  * @class Roo.QuickTips
15719  * Provides attractive and customizable tooltips for any element.
15720  * @singleton
15721  */
15722 Roo.QuickTips = function(){
15723     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15724     var ce, bd, xy, dd;
15725     var visible = false, disabled = true, inited = false;
15726     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15727     
15728     var onOver = function(e){
15729         if(disabled){
15730             return;
15731         }
15732         var t = e.getTarget();
15733         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15734             return;
15735         }
15736         if(ce && t == ce.el){
15737             clearTimeout(hideProc);
15738             return;
15739         }
15740         if(t && tagEls[t.id]){
15741             tagEls[t.id].el = t;
15742             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15743             return;
15744         }
15745         var ttp, et = Roo.fly(t);
15746         var ns = cfg.namespace;
15747         if(tm.interceptTitles && t.title){
15748             ttp = t.title;
15749             t.qtip = ttp;
15750             t.removeAttribute("title");
15751             e.preventDefault();
15752         }else{
15753             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15754         }
15755         if(ttp){
15756             showProc = show.defer(tm.showDelay, tm, [{
15757                 el: t, 
15758                 text: ttp, 
15759                 width: et.getAttributeNS(ns, cfg.width),
15760                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15761                 title: et.getAttributeNS(ns, cfg.title),
15762                     cls: et.getAttributeNS(ns, cfg.cls)
15763             }]);
15764         }
15765     };
15766     
15767     var onOut = function(e){
15768         clearTimeout(showProc);
15769         var t = e.getTarget();
15770         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15771             hideProc = setTimeout(hide, tm.hideDelay);
15772         }
15773     };
15774     
15775     var onMove = function(e){
15776         if(disabled){
15777             return;
15778         }
15779         xy = e.getXY();
15780         xy[1] += 18;
15781         if(tm.trackMouse && ce){
15782             el.setXY(xy);
15783         }
15784     };
15785     
15786     var onDown = function(e){
15787         clearTimeout(showProc);
15788         clearTimeout(hideProc);
15789         if(!e.within(el)){
15790             if(tm.hideOnClick){
15791                 hide();
15792                 tm.disable();
15793                 tm.enable.defer(100, tm);
15794             }
15795         }
15796     };
15797     
15798     var getPad = function(){
15799         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15800     };
15801
15802     var show = function(o){
15803         if(disabled){
15804             return;
15805         }
15806         clearTimeout(dismissProc);
15807         ce = o;
15808         if(removeCls){ // in case manually hidden
15809             el.removeClass(removeCls);
15810             removeCls = null;
15811         }
15812         if(ce.cls){
15813             el.addClass(ce.cls);
15814             removeCls = ce.cls;
15815         }
15816         if(ce.title){
15817             tipTitle.update(ce.title);
15818             tipTitle.show();
15819         }else{
15820             tipTitle.update('');
15821             tipTitle.hide();
15822         }
15823         el.dom.style.width  = tm.maxWidth+'px';
15824         //tipBody.dom.style.width = '';
15825         tipBodyText.update(o.text);
15826         var p = getPad(), w = ce.width;
15827         if(!w){
15828             var td = tipBodyText.dom;
15829             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15830             if(aw > tm.maxWidth){
15831                 w = tm.maxWidth;
15832             }else if(aw < tm.minWidth){
15833                 w = tm.minWidth;
15834             }else{
15835                 w = aw;
15836             }
15837         }
15838         //tipBody.setWidth(w);
15839         el.setWidth(parseInt(w, 10) + p);
15840         if(ce.autoHide === false){
15841             close.setDisplayed(true);
15842             if(dd){
15843                 dd.unlock();
15844             }
15845         }else{
15846             close.setDisplayed(false);
15847             if(dd){
15848                 dd.lock();
15849             }
15850         }
15851         if(xy){
15852             el.avoidY = xy[1]-18;
15853             el.setXY(xy);
15854         }
15855         if(tm.animate){
15856             el.setOpacity(.1);
15857             el.setStyle("visibility", "visible");
15858             el.fadeIn({callback: afterShow});
15859         }else{
15860             afterShow();
15861         }
15862     };
15863     
15864     var afterShow = function(){
15865         if(ce){
15866             el.show();
15867             esc.enable();
15868             if(tm.autoDismiss && ce.autoHide !== false){
15869                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15870             }
15871         }
15872     };
15873     
15874     var hide = function(noanim){
15875         clearTimeout(dismissProc);
15876         clearTimeout(hideProc);
15877         ce = null;
15878         if(el.isVisible()){
15879             esc.disable();
15880             if(noanim !== true && tm.animate){
15881                 el.fadeOut({callback: afterHide});
15882             }else{
15883                 afterHide();
15884             } 
15885         }
15886     };
15887     
15888     var afterHide = function(){
15889         el.hide();
15890         if(removeCls){
15891             el.removeClass(removeCls);
15892             removeCls = null;
15893         }
15894     };
15895     
15896     return {
15897         /**
15898         * @cfg {Number} minWidth
15899         * The minimum width of the quick tip (defaults to 40)
15900         */
15901        minWidth : 40,
15902         /**
15903         * @cfg {Number} maxWidth
15904         * The maximum width of the quick tip (defaults to 300)
15905         */
15906        maxWidth : 300,
15907         /**
15908         * @cfg {Boolean} interceptTitles
15909         * True to automatically use the element's DOM title value if available (defaults to false)
15910         */
15911        interceptTitles : false,
15912         /**
15913         * @cfg {Boolean} trackMouse
15914         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15915         */
15916        trackMouse : false,
15917         /**
15918         * @cfg {Boolean} hideOnClick
15919         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15920         */
15921        hideOnClick : true,
15922         /**
15923         * @cfg {Number} showDelay
15924         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15925         */
15926        showDelay : 500,
15927         /**
15928         * @cfg {Number} hideDelay
15929         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15930         */
15931        hideDelay : 200,
15932         /**
15933         * @cfg {Boolean} autoHide
15934         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15935         * Used in conjunction with hideDelay.
15936         */
15937        autoHide : true,
15938         /**
15939         * @cfg {Boolean}
15940         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15941         * (defaults to true).  Used in conjunction with autoDismissDelay.
15942         */
15943        autoDismiss : true,
15944         /**
15945         * @cfg {Number}
15946         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15947         */
15948        autoDismissDelay : 5000,
15949        /**
15950         * @cfg {Boolean} animate
15951         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15952         */
15953        animate : false,
15954
15955        /**
15956         * @cfg {String} title
15957         * Title text to display (defaults to '').  This can be any valid HTML markup.
15958         */
15959         title: '',
15960        /**
15961         * @cfg {String} text
15962         * Body text to display (defaults to '').  This can be any valid HTML markup.
15963         */
15964         text : '',
15965        /**
15966         * @cfg {String} cls
15967         * A CSS class to apply to the base quick tip element (defaults to '').
15968         */
15969         cls : '',
15970        /**
15971         * @cfg {Number} width
15972         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15973         * minWidth or maxWidth.
15974         */
15975         width : null,
15976
15977     /**
15978      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15979      * or display QuickTips in a page.
15980      */
15981        init : function(){
15982           tm = Roo.QuickTips;
15983           cfg = tm.tagConfig;
15984           if(!inited){
15985               if(!Roo.isReady){ // allow calling of init() before onReady
15986                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15987                   return;
15988               }
15989               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15990               el.fxDefaults = {stopFx: true};
15991               // maximum custom styling
15992               //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>');
15993               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>');              
15994               tipTitle = el.child('h3');
15995               tipTitle.enableDisplayMode("block");
15996               tipBody = el.child('div.x-tip-bd');
15997               tipBodyText = el.child('div.x-tip-bd-inner');
15998               //bdLeft = el.child('div.x-tip-bd-left');
15999               //bdRight = el.child('div.x-tip-bd-right');
16000               close = el.child('div.x-tip-close');
16001               close.enableDisplayMode("block");
16002               close.on("click", hide);
16003               var d = Roo.get(document);
16004               d.on("mousedown", onDown);
16005               d.on("mouseover", onOver);
16006               d.on("mouseout", onOut);
16007               d.on("mousemove", onMove);
16008               esc = d.addKeyListener(27, hide);
16009               esc.disable();
16010               if(Roo.dd.DD){
16011                   dd = el.initDD("default", null, {
16012                       onDrag : function(){
16013                           el.sync();  
16014                       }
16015                   });
16016                   dd.setHandleElId(tipTitle.id);
16017                   dd.lock();
16018               }
16019               inited = true;
16020           }
16021           this.enable(); 
16022        },
16023
16024     /**
16025      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16026      * are supported:
16027      * <pre>
16028 Property    Type                   Description
16029 ----------  ---------------------  ------------------------------------------------------------------------
16030 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16031      * </ul>
16032      * @param {Object} config The config object
16033      */
16034        register : function(config){
16035            var cs = config instanceof Array ? config : arguments;
16036            for(var i = 0, len = cs.length; i < len; i++) {
16037                var c = cs[i];
16038                var target = c.target;
16039                if(target){
16040                    if(target instanceof Array){
16041                        for(var j = 0, jlen = target.length; j < jlen; j++){
16042                            tagEls[target[j]] = c;
16043                        }
16044                    }else{
16045                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16046                    }
16047                }
16048            }
16049        },
16050
16051     /**
16052      * Removes this quick tip from its element and destroys it.
16053      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16054      */
16055        unregister : function(el){
16056            delete tagEls[Roo.id(el)];
16057        },
16058
16059     /**
16060      * Enable this quick tip.
16061      */
16062        enable : function(){
16063            if(inited && disabled){
16064                locks.pop();
16065                if(locks.length < 1){
16066                    disabled = false;
16067                }
16068            }
16069        },
16070
16071     /**
16072      * Disable this quick tip.
16073      */
16074        disable : function(){
16075           disabled = true;
16076           clearTimeout(showProc);
16077           clearTimeout(hideProc);
16078           clearTimeout(dismissProc);
16079           if(ce){
16080               hide(true);
16081           }
16082           locks.push(1);
16083        },
16084
16085     /**
16086      * Returns true if the quick tip is enabled, else false.
16087      */
16088        isEnabled : function(){
16089             return !disabled;
16090        },
16091
16092         // private
16093        tagConfig : {
16094            namespace : "ext",
16095            attribute : "qtip",
16096            width : "width",
16097            target : "target",
16098            title : "qtitle",
16099            hide : "hide",
16100            cls : "qclass"
16101        }
16102    };
16103 }();
16104
16105 // backwards compat
16106 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16107  * Based on:
16108  * Ext JS Library 1.1.1
16109  * Copyright(c) 2006-2007, Ext JS, LLC.
16110  *
16111  * Originally Released Under LGPL - original licence link has changed is not relivant.
16112  *
16113  * Fork - LGPL
16114  * <script type="text/javascript">
16115  */
16116  
16117
16118 /**
16119  * @class Roo.tree.TreePanel
16120  * @extends Roo.data.Tree
16121
16122  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16123  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16124  * @cfg {Boolean} enableDD true to enable drag and drop
16125  * @cfg {Boolean} enableDrag true to enable just drag
16126  * @cfg {Boolean} enableDrop true to enable just drop
16127  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16128  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16129  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16130  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16131  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16132  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16133  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16134  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16135  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16136  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16137  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16138  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16139  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16140  * @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>
16141  * @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>
16142  * 
16143  * @constructor
16144  * @param {String/HTMLElement/Element} el The container element
16145  * @param {Object} config
16146  */
16147 Roo.tree.TreePanel = function(el, config){
16148     var root = false;
16149     var loader = false;
16150     if (config.root) {
16151         root = config.root;
16152         delete config.root;
16153     }
16154     if (config.loader) {
16155         loader = config.loader;
16156         delete config.loader;
16157     }
16158     
16159     Roo.apply(this, config);
16160     Roo.tree.TreePanel.superclass.constructor.call(this);
16161     this.el = Roo.get(el);
16162     this.el.addClass('x-tree');
16163     //console.log(root);
16164     if (root) {
16165         this.setRootNode( Roo.factory(root, Roo.tree));
16166     }
16167     if (loader) {
16168         this.loader = Roo.factory(loader, Roo.tree);
16169     }
16170    /**
16171     * Read-only. The id of the container element becomes this TreePanel's id.
16172     */
16173    this.id = this.el.id;
16174    this.addEvents({
16175         /**
16176         * @event beforeload
16177         * Fires before a node is loaded, return false to cancel
16178         * @param {Node} node The node being loaded
16179         */
16180         "beforeload" : true,
16181         /**
16182         * @event load
16183         * Fires when a node is loaded
16184         * @param {Node} node The node that was loaded
16185         */
16186         "load" : true,
16187         /**
16188         * @event textchange
16189         * Fires when the text for a node is changed
16190         * @param {Node} node The node
16191         * @param {String} text The new text
16192         * @param {String} oldText The old text
16193         */
16194         "textchange" : true,
16195         /**
16196         * @event beforeexpand
16197         * Fires before a node is expanded, return false to cancel.
16198         * @param {Node} node The node
16199         * @param {Boolean} deep
16200         * @param {Boolean} anim
16201         */
16202         "beforeexpand" : true,
16203         /**
16204         * @event beforecollapse
16205         * Fires before a node is collapsed, return false to cancel.
16206         * @param {Node} node The node
16207         * @param {Boolean} deep
16208         * @param {Boolean} anim
16209         */
16210         "beforecollapse" : true,
16211         /**
16212         * @event expand
16213         * Fires when a node is expanded
16214         * @param {Node} node The node
16215         */
16216         "expand" : true,
16217         /**
16218         * @event disabledchange
16219         * Fires when the disabled status of a node changes
16220         * @param {Node} node The node
16221         * @param {Boolean} disabled
16222         */
16223         "disabledchange" : true,
16224         /**
16225         * @event collapse
16226         * Fires when a node is collapsed
16227         * @param {Node} node The node
16228         */
16229         "collapse" : true,
16230         /**
16231         * @event beforeclick
16232         * Fires before click processing on a node. Return false to cancel the default action.
16233         * @param {Node} node The node
16234         * @param {Roo.EventObject} e The event object
16235         */
16236         "beforeclick":true,
16237         /**
16238         * @event checkchange
16239         * Fires when a node with a checkbox's checked property changes
16240         * @param {Node} this This node
16241         * @param {Boolean} checked
16242         */
16243         "checkchange":true,
16244         /**
16245         * @event click
16246         * Fires when a node is clicked
16247         * @param {Node} node The node
16248         * @param {Roo.EventObject} e The event object
16249         */
16250         "click":true,
16251         /**
16252         * @event dblclick
16253         * Fires when a node is double clicked
16254         * @param {Node} node The node
16255         * @param {Roo.EventObject} e The event object
16256         */
16257         "dblclick":true,
16258         /**
16259         * @event contextmenu
16260         * Fires when a node is right clicked
16261         * @param {Node} node The node
16262         * @param {Roo.EventObject} e The event object
16263         */
16264         "contextmenu":true,
16265         /**
16266         * @event beforechildrenrendered
16267         * Fires right before the child nodes for a node are rendered
16268         * @param {Node} node The node
16269         */
16270         "beforechildrenrendered":true,
16271        /**
16272              * @event startdrag
16273              * Fires when a node starts being dragged
16274              * @param {Roo.tree.TreePanel} this
16275              * @param {Roo.tree.TreeNode} node
16276              * @param {event} e The raw browser event
16277              */ 
16278             "startdrag" : true,
16279             /**
16280              * @event enddrag
16281              * Fires when a drag operation is complete
16282              * @param {Roo.tree.TreePanel} this
16283              * @param {Roo.tree.TreeNode} node
16284              * @param {event} e The raw browser event
16285              */
16286             "enddrag" : true,
16287             /**
16288              * @event dragdrop
16289              * Fires when a dragged node is dropped on a valid DD target
16290              * @param {Roo.tree.TreePanel} this
16291              * @param {Roo.tree.TreeNode} node
16292              * @param {DD} dd The dd it was dropped on
16293              * @param {event} e The raw browser event
16294              */
16295             "dragdrop" : true,
16296             /**
16297              * @event beforenodedrop
16298              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16299              * passed to handlers has the following properties:<br />
16300              * <ul style="padding:5px;padding-left:16px;">
16301              * <li>tree - The TreePanel</li>
16302              * <li>target - The node being targeted for the drop</li>
16303              * <li>data - The drag data from the drag source</li>
16304              * <li>point - The point of the drop - append, above or below</li>
16305              * <li>source - The drag source</li>
16306              * <li>rawEvent - Raw mouse event</li>
16307              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16308              * to be inserted by setting them on this object.</li>
16309              * <li>cancel - Set this to true to cancel the drop.</li>
16310              * </ul>
16311              * @param {Object} dropEvent
16312              */
16313             "beforenodedrop" : true,
16314             /**
16315              * @event nodedrop
16316              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16317              * passed to handlers has the following properties:<br />
16318              * <ul style="padding:5px;padding-left:16px;">
16319              * <li>tree - The TreePanel</li>
16320              * <li>target - The node being targeted for the drop</li>
16321              * <li>data - The drag data from the drag source</li>
16322              * <li>point - The point of the drop - append, above or below</li>
16323              * <li>source - The drag source</li>
16324              * <li>rawEvent - Raw mouse event</li>
16325              * <li>dropNode - Dropped node(s).</li>
16326              * </ul>
16327              * @param {Object} dropEvent
16328              */
16329             "nodedrop" : true,
16330              /**
16331              * @event nodedragover
16332              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16333              * passed to handlers has the following properties:<br />
16334              * <ul style="padding:5px;padding-left:16px;">
16335              * <li>tree - The TreePanel</li>
16336              * <li>target - The node being targeted for the drop</li>
16337              * <li>data - The drag data from the drag source</li>
16338              * <li>point - The point of the drop - append, above or below</li>
16339              * <li>source - The drag source</li>
16340              * <li>rawEvent - Raw mouse event</li>
16341              * <li>dropNode - Drop node(s) provided by the source.</li>
16342              * <li>cancel - Set this to true to signal drop not allowed.</li>
16343              * </ul>
16344              * @param {Object} dragOverEvent
16345              */
16346             "nodedragover" : true
16347         
16348    });
16349    if(this.singleExpand){
16350        this.on("beforeexpand", this.restrictExpand, this);
16351    }
16352 };
16353 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16354     rootVisible : true,
16355     animate: Roo.enableFx,
16356     lines : true,
16357     enableDD : false,
16358     hlDrop : Roo.enableFx,
16359   
16360     renderer: false,
16361     
16362     rendererTip: false,
16363     // private
16364     restrictExpand : function(node){
16365         var p = node.parentNode;
16366         if(p){
16367             if(p.expandedChild && p.expandedChild.parentNode == p){
16368                 p.expandedChild.collapse();
16369             }
16370             p.expandedChild = node;
16371         }
16372     },
16373
16374     // private override
16375     setRootNode : function(node){
16376         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16377         if(!this.rootVisible){
16378             node.ui = new Roo.tree.RootTreeNodeUI(node);
16379         }
16380         return node;
16381     },
16382
16383     /**
16384      * Returns the container element for this TreePanel
16385      */
16386     getEl : function(){
16387         return this.el;
16388     },
16389
16390     /**
16391      * Returns the default TreeLoader for this TreePanel
16392      */
16393     getLoader : function(){
16394         return this.loader;
16395     },
16396
16397     /**
16398      * Expand all nodes
16399      */
16400     expandAll : function(){
16401         this.root.expand(true);
16402     },
16403
16404     /**
16405      * Collapse all nodes
16406      */
16407     collapseAll : function(){
16408         this.root.collapse(true);
16409     },
16410
16411     /**
16412      * Returns the selection model used by this TreePanel
16413      */
16414     getSelectionModel : function(){
16415         if(!this.selModel){
16416             this.selModel = new Roo.tree.DefaultSelectionModel();
16417         }
16418         return this.selModel;
16419     },
16420
16421     /**
16422      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16423      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16424      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16425      * @return {Array}
16426      */
16427     getChecked : function(a, startNode){
16428         startNode = startNode || this.root;
16429         var r = [];
16430         var f = function(){
16431             if(this.attributes.checked){
16432                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16433             }
16434         }
16435         startNode.cascade(f);
16436         return r;
16437     },
16438
16439     /**
16440      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16441      * @param {String} path
16442      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16443      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16444      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16445      */
16446     expandPath : function(path, attr, callback){
16447         attr = attr || "id";
16448         var keys = path.split(this.pathSeparator);
16449         var curNode = this.root;
16450         if(curNode.attributes[attr] != keys[1]){ // invalid root
16451             if(callback){
16452                 callback(false, null);
16453             }
16454             return;
16455         }
16456         var index = 1;
16457         var f = function(){
16458             if(++index == keys.length){
16459                 if(callback){
16460                     callback(true, curNode);
16461                 }
16462                 return;
16463             }
16464             var c = curNode.findChild(attr, keys[index]);
16465             if(!c){
16466                 if(callback){
16467                     callback(false, curNode);
16468                 }
16469                 return;
16470             }
16471             curNode = c;
16472             c.expand(false, false, f);
16473         };
16474         curNode.expand(false, false, f);
16475     },
16476
16477     /**
16478      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16479      * @param {String} path
16480      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16481      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16482      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16483      */
16484     selectPath : function(path, attr, callback){
16485         attr = attr || "id";
16486         var keys = path.split(this.pathSeparator);
16487         var v = keys.pop();
16488         if(keys.length > 0){
16489             var f = function(success, node){
16490                 if(success && node){
16491                     var n = node.findChild(attr, v);
16492                     if(n){
16493                         n.select();
16494                         if(callback){
16495                             callback(true, n);
16496                         }
16497                     }else if(callback){
16498                         callback(false, n);
16499                     }
16500                 }else{
16501                     if(callback){
16502                         callback(false, n);
16503                     }
16504                 }
16505             };
16506             this.expandPath(keys.join(this.pathSeparator), attr, f);
16507         }else{
16508             this.root.select();
16509             if(callback){
16510                 callback(true, this.root);
16511             }
16512         }
16513     },
16514
16515     getTreeEl : function(){
16516         return this.el;
16517     },
16518
16519     /**
16520      * Trigger rendering of this TreePanel
16521      */
16522     render : function(){
16523         if (this.innerCt) {
16524             return this; // stop it rendering more than once!!
16525         }
16526         
16527         this.innerCt = this.el.createChild({tag:"ul",
16528                cls:"x-tree-root-ct " +
16529                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16530
16531         if(this.containerScroll){
16532             Roo.dd.ScrollManager.register(this.el);
16533         }
16534         if((this.enableDD || this.enableDrop) && !this.dropZone){
16535            /**
16536             * The dropZone used by this tree if drop is enabled
16537             * @type Roo.tree.TreeDropZone
16538             */
16539              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16540                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16541            });
16542         }
16543         if((this.enableDD || this.enableDrag) && !this.dragZone){
16544            /**
16545             * The dragZone used by this tree if drag is enabled
16546             * @type Roo.tree.TreeDragZone
16547             */
16548             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16549                ddGroup: this.ddGroup || "TreeDD",
16550                scroll: this.ddScroll
16551            });
16552         }
16553         this.getSelectionModel().init(this);
16554         if (!this.root) {
16555             console.log("ROOT not set in tree");
16556             return;
16557         }
16558         this.root.render();
16559         if(!this.rootVisible){
16560             this.root.renderChildren();
16561         }
16562         return this;
16563     }
16564 });/*
16565  * Based on:
16566  * Ext JS Library 1.1.1
16567  * Copyright(c) 2006-2007, Ext JS, LLC.
16568  *
16569  * Originally Released Under LGPL - original licence link has changed is not relivant.
16570  *
16571  * Fork - LGPL
16572  * <script type="text/javascript">
16573  */
16574  
16575
16576 /**
16577  * @class Roo.tree.DefaultSelectionModel
16578  * @extends Roo.util.Observable
16579  * The default single selection for a TreePanel.
16580  */
16581 Roo.tree.DefaultSelectionModel = function(){
16582    this.selNode = null;
16583    
16584    this.addEvents({
16585        /**
16586         * @event selectionchange
16587         * Fires when the selected node changes
16588         * @param {DefaultSelectionModel} this
16589         * @param {TreeNode} node the new selection
16590         */
16591        "selectionchange" : true,
16592
16593        /**
16594         * @event beforeselect
16595         * Fires before the selected node changes, return false to cancel the change
16596         * @param {DefaultSelectionModel} this
16597         * @param {TreeNode} node the new selection
16598         * @param {TreeNode} node the old selection
16599         */
16600        "beforeselect" : true
16601    });
16602 };
16603
16604 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16605     init : function(tree){
16606         this.tree = tree;
16607         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16608         tree.on("click", this.onNodeClick, this);
16609     },
16610     
16611     onNodeClick : function(node, e){
16612         if (e.ctrlKey && this.selNode == node)  {
16613             this.unselect(node);
16614             return;
16615         }
16616         this.select(node);
16617     },
16618     
16619     /**
16620      * Select a node.
16621      * @param {TreeNode} node The node to select
16622      * @return {TreeNode} The selected node
16623      */
16624     select : function(node){
16625         var last = this.selNode;
16626         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16627             if(last){
16628                 last.ui.onSelectedChange(false);
16629             }
16630             this.selNode = node;
16631             node.ui.onSelectedChange(true);
16632             this.fireEvent("selectionchange", this, node, last);
16633         }
16634         return node;
16635     },
16636     
16637     /**
16638      * Deselect a node.
16639      * @param {TreeNode} node The node to unselect
16640      */
16641     unselect : function(node){
16642         if(this.selNode == node){
16643             this.clearSelections();
16644         }    
16645     },
16646     
16647     /**
16648      * Clear all selections
16649      */
16650     clearSelections : function(){
16651         var n = this.selNode;
16652         if(n){
16653             n.ui.onSelectedChange(false);
16654             this.selNode = null;
16655             this.fireEvent("selectionchange", this, null);
16656         }
16657         return n;
16658     },
16659     
16660     /**
16661      * Get the selected node
16662      * @return {TreeNode} The selected node
16663      */
16664     getSelectedNode : function(){
16665         return this.selNode;    
16666     },
16667     
16668     /**
16669      * Returns true if the node is selected
16670      * @param {TreeNode} node The node to check
16671      * @return {Boolean}
16672      */
16673     isSelected : function(node){
16674         return this.selNode == node;  
16675     },
16676
16677     /**
16678      * Selects the node above the selected node in the tree, intelligently walking the nodes
16679      * @return TreeNode The new selection
16680      */
16681     selectPrevious : function(){
16682         var s = this.selNode || this.lastSelNode;
16683         if(!s){
16684             return null;
16685         }
16686         var ps = s.previousSibling;
16687         if(ps){
16688             if(!ps.isExpanded() || ps.childNodes.length < 1){
16689                 return this.select(ps);
16690             } else{
16691                 var lc = ps.lastChild;
16692                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16693                     lc = lc.lastChild;
16694                 }
16695                 return this.select(lc);
16696             }
16697         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16698             return this.select(s.parentNode);
16699         }
16700         return null;
16701     },
16702
16703     /**
16704      * Selects the node above the selected node in the tree, intelligently walking the nodes
16705      * @return TreeNode The new selection
16706      */
16707     selectNext : function(){
16708         var s = this.selNode || this.lastSelNode;
16709         if(!s){
16710             return null;
16711         }
16712         if(s.firstChild && s.isExpanded()){
16713              return this.select(s.firstChild);
16714          }else if(s.nextSibling){
16715              return this.select(s.nextSibling);
16716          }else if(s.parentNode){
16717             var newS = null;
16718             s.parentNode.bubble(function(){
16719                 if(this.nextSibling){
16720                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16721                     return false;
16722                 }
16723             });
16724             return newS;
16725          }
16726         return null;
16727     },
16728
16729     onKeyDown : function(e){
16730         var s = this.selNode || this.lastSelNode;
16731         // undesirable, but required
16732         var sm = this;
16733         if(!s){
16734             return;
16735         }
16736         var k = e.getKey();
16737         switch(k){
16738              case e.DOWN:
16739                  e.stopEvent();
16740                  this.selectNext();
16741              break;
16742              case e.UP:
16743                  e.stopEvent();
16744                  this.selectPrevious();
16745              break;
16746              case e.RIGHT:
16747                  e.preventDefault();
16748                  if(s.hasChildNodes()){
16749                      if(!s.isExpanded()){
16750                          s.expand();
16751                      }else if(s.firstChild){
16752                          this.select(s.firstChild, e);
16753                      }
16754                  }
16755              break;
16756              case e.LEFT:
16757                  e.preventDefault();
16758                  if(s.hasChildNodes() && s.isExpanded()){
16759                      s.collapse();
16760                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16761                      this.select(s.parentNode, e);
16762                  }
16763              break;
16764         };
16765     }
16766 });
16767
16768 /**
16769  * @class Roo.tree.MultiSelectionModel
16770  * @extends Roo.util.Observable
16771  * Multi selection for a TreePanel.
16772  */
16773 Roo.tree.MultiSelectionModel = function(){
16774    this.selNodes = [];
16775    this.selMap = {};
16776    this.addEvents({
16777        /**
16778         * @event selectionchange
16779         * Fires when the selected nodes change
16780         * @param {MultiSelectionModel} this
16781         * @param {Array} nodes Array of the selected nodes
16782         */
16783        "selectionchange" : true
16784    });
16785 };
16786
16787 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16788     init : function(tree){
16789         this.tree = tree;
16790         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16791         tree.on("click", this.onNodeClick, this);
16792     },
16793     
16794     onNodeClick : function(node, e){
16795         this.select(node, e, e.ctrlKey);
16796     },
16797     
16798     /**
16799      * Select a node.
16800      * @param {TreeNode} node The node to select
16801      * @param {EventObject} e (optional) An event associated with the selection
16802      * @param {Boolean} keepExisting True to retain existing selections
16803      * @return {TreeNode} The selected node
16804      */
16805     select : function(node, e, keepExisting){
16806         if(keepExisting !== true){
16807             this.clearSelections(true);
16808         }
16809         if(this.isSelected(node)){
16810             this.lastSelNode = node;
16811             return node;
16812         }
16813         this.selNodes.push(node);
16814         this.selMap[node.id] = node;
16815         this.lastSelNode = node;
16816         node.ui.onSelectedChange(true);
16817         this.fireEvent("selectionchange", this, this.selNodes);
16818         return node;
16819     },
16820     
16821     /**
16822      * Deselect a node.
16823      * @param {TreeNode} node The node to unselect
16824      */
16825     unselect : function(node){
16826         if(this.selMap[node.id]){
16827             node.ui.onSelectedChange(false);
16828             var sn = this.selNodes;
16829             var index = -1;
16830             if(sn.indexOf){
16831                 index = sn.indexOf(node);
16832             }else{
16833                 for(var i = 0, len = sn.length; i < len; i++){
16834                     if(sn[i] == node){
16835                         index = i;
16836                         break;
16837                     }
16838                 }
16839             }
16840             if(index != -1){
16841                 this.selNodes.splice(index, 1);
16842             }
16843             delete this.selMap[node.id];
16844             this.fireEvent("selectionchange", this, this.selNodes);
16845         }
16846     },
16847     
16848     /**
16849      * Clear all selections
16850      */
16851     clearSelections : function(suppressEvent){
16852         var sn = this.selNodes;
16853         if(sn.length > 0){
16854             for(var i = 0, len = sn.length; i < len; i++){
16855                 sn[i].ui.onSelectedChange(false);
16856             }
16857             this.selNodes = [];
16858             this.selMap = {};
16859             if(suppressEvent !== true){
16860                 this.fireEvent("selectionchange", this, this.selNodes);
16861             }
16862         }
16863     },
16864     
16865     /**
16866      * Returns true if the node is selected
16867      * @param {TreeNode} node The node to check
16868      * @return {Boolean}
16869      */
16870     isSelected : function(node){
16871         return this.selMap[node.id] ? true : false;  
16872     },
16873     
16874     /**
16875      * Returns an array of the selected nodes
16876      * @return {Array}
16877      */
16878     getSelectedNodes : function(){
16879         return this.selNodes;    
16880     },
16881
16882     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16883
16884     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16885
16886     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16887 });/*
16888  * Based on:
16889  * Ext JS Library 1.1.1
16890  * Copyright(c) 2006-2007, Ext JS, LLC.
16891  *
16892  * Originally Released Under LGPL - original licence link has changed is not relivant.
16893  *
16894  * Fork - LGPL
16895  * <script type="text/javascript">
16896  */
16897  
16898 /**
16899  * @class Roo.tree.TreeNode
16900  * @extends Roo.data.Node
16901  * @cfg {String} text The text for this node
16902  * @cfg {Boolean} expanded true to start the node expanded
16903  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16904  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16905  * @cfg {Boolean} disabled true to start the node disabled
16906  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16907  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16908  * @cfg {String} cls A css class to be added to the node
16909  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16910  * @cfg {String} href URL of the link used for the node (defaults to #)
16911  * @cfg {String} hrefTarget target frame for the link
16912  * @cfg {String} qtip An Ext QuickTip for the node
16913  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16914  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16915  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16916  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16917  * (defaults to undefined with no checkbox rendered)
16918  * @constructor
16919  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16920  */
16921 Roo.tree.TreeNode = function(attributes){
16922     attributes = attributes || {};
16923     if(typeof attributes == "string"){
16924         attributes = {text: attributes};
16925     }
16926     this.childrenRendered = false;
16927     this.rendered = false;
16928     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16929     this.expanded = attributes.expanded === true;
16930     this.isTarget = attributes.isTarget !== false;
16931     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16932     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16933
16934     /**
16935      * Read-only. The text for this node. To change it use setText().
16936      * @type String
16937      */
16938     this.text = attributes.text;
16939     /**
16940      * True if this node is disabled.
16941      * @type Boolean
16942      */
16943     this.disabled = attributes.disabled === true;
16944
16945     this.addEvents({
16946         /**
16947         * @event textchange
16948         * Fires when the text for this node is changed
16949         * @param {Node} this This node
16950         * @param {String} text The new text
16951         * @param {String} oldText The old text
16952         */
16953         "textchange" : true,
16954         /**
16955         * @event beforeexpand
16956         * Fires before this node is expanded, return false to cancel.
16957         * @param {Node} this This node
16958         * @param {Boolean} deep
16959         * @param {Boolean} anim
16960         */
16961         "beforeexpand" : true,
16962         /**
16963         * @event beforecollapse
16964         * Fires before this node is collapsed, return false to cancel.
16965         * @param {Node} this This node
16966         * @param {Boolean} deep
16967         * @param {Boolean} anim
16968         */
16969         "beforecollapse" : true,
16970         /**
16971         * @event expand
16972         * Fires when this node is expanded
16973         * @param {Node} this This node
16974         */
16975         "expand" : true,
16976         /**
16977         * @event disabledchange
16978         * Fires when the disabled status of this node changes
16979         * @param {Node} this This node
16980         * @param {Boolean} disabled
16981         */
16982         "disabledchange" : true,
16983         /**
16984         * @event collapse
16985         * Fires when this node is collapsed
16986         * @param {Node} this This node
16987         */
16988         "collapse" : true,
16989         /**
16990         * @event beforeclick
16991         * Fires before click processing. Return false to cancel the default action.
16992         * @param {Node} this This node
16993         * @param {Roo.EventObject} e The event object
16994         */
16995         "beforeclick":true,
16996         /**
16997         * @event checkchange
16998         * Fires when a node with a checkbox's checked property changes
16999         * @param {Node} this This node
17000         * @param {Boolean} checked
17001         */
17002         "checkchange":true,
17003         /**
17004         * @event click
17005         * Fires when this node is clicked
17006         * @param {Node} this This node
17007         * @param {Roo.EventObject} e The event object
17008         */
17009         "click":true,
17010         /**
17011         * @event dblclick
17012         * Fires when this node is double clicked
17013         * @param {Node} this This node
17014         * @param {Roo.EventObject} e The event object
17015         */
17016         "dblclick":true,
17017         /**
17018         * @event contextmenu
17019         * Fires when this node is right clicked
17020         * @param {Node} this This node
17021         * @param {Roo.EventObject} e The event object
17022         */
17023         "contextmenu":true,
17024         /**
17025         * @event beforechildrenrendered
17026         * Fires right before the child nodes for this node are rendered
17027         * @param {Node} this This node
17028         */
17029         "beforechildrenrendered":true
17030     });
17031
17032     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17033
17034     /**
17035      * Read-only. The UI for this node
17036      * @type TreeNodeUI
17037      */
17038     this.ui = new uiClass(this);
17039 };
17040 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17041     preventHScroll: true,
17042     /**
17043      * Returns true if this node is expanded
17044      * @return {Boolean}
17045      */
17046     isExpanded : function(){
17047         return this.expanded;
17048     },
17049
17050     /**
17051      * Returns the UI object for this node
17052      * @return {TreeNodeUI}
17053      */
17054     getUI : function(){
17055         return this.ui;
17056     },
17057
17058     // private override
17059     setFirstChild : function(node){
17060         var of = this.firstChild;
17061         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17062         if(this.childrenRendered && of && node != of){
17063             of.renderIndent(true, true);
17064         }
17065         if(this.rendered){
17066             this.renderIndent(true, true);
17067         }
17068     },
17069
17070     // private override
17071     setLastChild : function(node){
17072         var ol = this.lastChild;
17073         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17074         if(this.childrenRendered && ol && node != ol){
17075             ol.renderIndent(true, true);
17076         }
17077         if(this.rendered){
17078             this.renderIndent(true, true);
17079         }
17080     },
17081
17082     // these methods are overridden to provide lazy rendering support
17083     // private override
17084     appendChild : function(){
17085         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17086         if(node && this.childrenRendered){
17087             node.render();
17088         }
17089         this.ui.updateExpandIcon();
17090         return node;
17091     },
17092
17093     // private override
17094     removeChild : function(node){
17095         this.ownerTree.getSelectionModel().unselect(node);
17096         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17097         // if it's been rendered remove dom node
17098         if(this.childrenRendered){
17099             node.ui.remove();
17100         }
17101         if(this.childNodes.length < 1){
17102             this.collapse(false, false);
17103         }else{
17104             this.ui.updateExpandIcon();
17105         }
17106         if(!this.firstChild) {
17107             this.childrenRendered = false;
17108         }
17109         return node;
17110     },
17111
17112     // private override
17113     insertBefore : function(node, refNode){
17114         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17115         if(newNode && refNode && this.childrenRendered){
17116             node.render();
17117         }
17118         this.ui.updateExpandIcon();
17119         return newNode;
17120     },
17121
17122     /**
17123      * Sets the text for this node
17124      * @param {String} text
17125      */
17126     setText : function(text){
17127         var oldText = this.text;
17128         this.text = text;
17129         this.attributes.text = text;
17130         if(this.rendered){ // event without subscribing
17131             this.ui.onTextChange(this, text, oldText);
17132         }
17133         this.fireEvent("textchange", this, text, oldText);
17134     },
17135
17136     /**
17137      * Triggers selection of this node
17138      */
17139     select : function(){
17140         this.getOwnerTree().getSelectionModel().select(this);
17141     },
17142
17143     /**
17144      * Triggers deselection of this node
17145      */
17146     unselect : function(){
17147         this.getOwnerTree().getSelectionModel().unselect(this);
17148     },
17149
17150     /**
17151      * Returns true if this node is selected
17152      * @return {Boolean}
17153      */
17154     isSelected : function(){
17155         return this.getOwnerTree().getSelectionModel().isSelected(this);
17156     },
17157
17158     /**
17159      * Expand this node.
17160      * @param {Boolean} deep (optional) True to expand all children as well
17161      * @param {Boolean} anim (optional) false to cancel the default animation
17162      * @param {Function} callback (optional) A callback to be called when
17163      * expanding this node completes (does not wait for deep expand to complete).
17164      * Called with 1 parameter, this node.
17165      */
17166     expand : function(deep, anim, callback){
17167         if(!this.expanded){
17168             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17169                 return;
17170             }
17171             if(!this.childrenRendered){
17172                 this.renderChildren();
17173             }
17174             this.expanded = true;
17175             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17176                 this.ui.animExpand(function(){
17177                     this.fireEvent("expand", this);
17178                     if(typeof callback == "function"){
17179                         callback(this);
17180                     }
17181                     if(deep === true){
17182                         this.expandChildNodes(true);
17183                     }
17184                 }.createDelegate(this));
17185                 return;
17186             }else{
17187                 this.ui.expand();
17188                 this.fireEvent("expand", this);
17189                 if(typeof callback == "function"){
17190                     callback(this);
17191                 }
17192             }
17193         }else{
17194            if(typeof callback == "function"){
17195                callback(this);
17196            }
17197         }
17198         if(deep === true){
17199             this.expandChildNodes(true);
17200         }
17201     },
17202
17203     isHiddenRoot : function(){
17204         return this.isRoot && !this.getOwnerTree().rootVisible;
17205     },
17206
17207     /**
17208      * Collapse this node.
17209      * @param {Boolean} deep (optional) True to collapse all children as well
17210      * @param {Boolean} anim (optional) false to cancel the default animation
17211      */
17212     collapse : function(deep, anim){
17213         if(this.expanded && !this.isHiddenRoot()){
17214             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17215                 return;
17216             }
17217             this.expanded = false;
17218             if((this.getOwnerTree().animate && anim !== false) || anim){
17219                 this.ui.animCollapse(function(){
17220                     this.fireEvent("collapse", this);
17221                     if(deep === true){
17222                         this.collapseChildNodes(true);
17223                     }
17224                 }.createDelegate(this));
17225                 return;
17226             }else{
17227                 this.ui.collapse();
17228                 this.fireEvent("collapse", this);
17229             }
17230         }
17231         if(deep === true){
17232             var cs = this.childNodes;
17233             for(var i = 0, len = cs.length; i < len; i++) {
17234                 cs[i].collapse(true, false);
17235             }
17236         }
17237     },
17238
17239     // private
17240     delayedExpand : function(delay){
17241         if(!this.expandProcId){
17242             this.expandProcId = this.expand.defer(delay, this);
17243         }
17244     },
17245
17246     // private
17247     cancelExpand : function(){
17248         if(this.expandProcId){
17249             clearTimeout(this.expandProcId);
17250         }
17251         this.expandProcId = false;
17252     },
17253
17254     /**
17255      * Toggles expanded/collapsed state of the node
17256      */
17257     toggle : function(){
17258         if(this.expanded){
17259             this.collapse();
17260         }else{
17261             this.expand();
17262         }
17263     },
17264
17265     /**
17266      * Ensures all parent nodes are expanded
17267      */
17268     ensureVisible : function(callback){
17269         var tree = this.getOwnerTree();
17270         tree.expandPath(this.parentNode.getPath(), false, function(){
17271             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17272             Roo.callback(callback);
17273         }.createDelegate(this));
17274     },
17275
17276     /**
17277      * Expand all child nodes
17278      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17279      */
17280     expandChildNodes : function(deep){
17281         var cs = this.childNodes;
17282         for(var i = 0, len = cs.length; i < len; i++) {
17283                 cs[i].expand(deep);
17284         }
17285     },
17286
17287     /**
17288      * Collapse all child nodes
17289      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17290      */
17291     collapseChildNodes : function(deep){
17292         var cs = this.childNodes;
17293         for(var i = 0, len = cs.length; i < len; i++) {
17294                 cs[i].collapse(deep);
17295         }
17296     },
17297
17298     /**
17299      * Disables this node
17300      */
17301     disable : function(){
17302         this.disabled = true;
17303         this.unselect();
17304         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17305             this.ui.onDisableChange(this, true);
17306         }
17307         this.fireEvent("disabledchange", this, true);
17308     },
17309
17310     /**
17311      * Enables this node
17312      */
17313     enable : function(){
17314         this.disabled = false;
17315         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17316             this.ui.onDisableChange(this, false);
17317         }
17318         this.fireEvent("disabledchange", this, false);
17319     },
17320
17321     // private
17322     renderChildren : function(suppressEvent){
17323         if(suppressEvent !== false){
17324             this.fireEvent("beforechildrenrendered", this);
17325         }
17326         var cs = this.childNodes;
17327         for(var i = 0, len = cs.length; i < len; i++){
17328             cs[i].render(true);
17329         }
17330         this.childrenRendered = true;
17331     },
17332
17333     // private
17334     sort : function(fn, scope){
17335         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17336         if(this.childrenRendered){
17337             var cs = this.childNodes;
17338             for(var i = 0, len = cs.length; i < len; i++){
17339                 cs[i].render(true);
17340             }
17341         }
17342     },
17343
17344     // private
17345     render : function(bulkRender){
17346         this.ui.render(bulkRender);
17347         if(!this.rendered){
17348             this.rendered = true;
17349             if(this.expanded){
17350                 this.expanded = false;
17351                 this.expand(false, false);
17352             }
17353         }
17354     },
17355
17356     // private
17357     renderIndent : function(deep, refresh){
17358         if(refresh){
17359             this.ui.childIndent = null;
17360         }
17361         this.ui.renderIndent();
17362         if(deep === true && this.childrenRendered){
17363             var cs = this.childNodes;
17364             for(var i = 0, len = cs.length; i < len; i++){
17365                 cs[i].renderIndent(true, refresh);
17366             }
17367         }
17368     }
17369 });/*
17370  * Based on:
17371  * Ext JS Library 1.1.1
17372  * Copyright(c) 2006-2007, Ext JS, LLC.
17373  *
17374  * Originally Released Under LGPL - original licence link has changed is not relivant.
17375  *
17376  * Fork - LGPL
17377  * <script type="text/javascript">
17378  */
17379  
17380 /**
17381  * @class Roo.tree.AsyncTreeNode
17382  * @extends Roo.tree.TreeNode
17383  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17384  * @constructor
17385  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17386  */
17387  Roo.tree.AsyncTreeNode = function(config){
17388     this.loaded = false;
17389     this.loading = false;
17390     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17391     /**
17392     * @event beforeload
17393     * Fires before this node is loaded, return false to cancel
17394     * @param {Node} this This node
17395     */
17396     this.addEvents({'beforeload':true, 'load': true});
17397     /**
17398     * @event load
17399     * Fires when this node is loaded
17400     * @param {Node} this This node
17401     */
17402     /**
17403      * The loader used by this node (defaults to using the tree's defined loader)
17404      * @type TreeLoader
17405      * @property loader
17406      */
17407 };
17408 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17409     expand : function(deep, anim, callback){
17410         if(this.loading){ // if an async load is already running, waiting til it's done
17411             var timer;
17412             var f = function(){
17413                 if(!this.loading){ // done loading
17414                     clearInterval(timer);
17415                     this.expand(deep, anim, callback);
17416                 }
17417             }.createDelegate(this);
17418             timer = setInterval(f, 200);
17419             return;
17420         }
17421         if(!this.loaded){
17422             if(this.fireEvent("beforeload", this) === false){
17423                 return;
17424             }
17425             this.loading = true;
17426             this.ui.beforeLoad(this);
17427             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17428             if(loader){
17429                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17430                 return;
17431             }
17432         }
17433         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17434     },
17435     
17436     /**
17437      * Returns true if this node is currently loading
17438      * @return {Boolean}
17439      */
17440     isLoading : function(){
17441         return this.loading;  
17442     },
17443     
17444     loadComplete : function(deep, anim, callback){
17445         this.loading = false;
17446         this.loaded = true;
17447         this.ui.afterLoad(this);
17448         this.fireEvent("load", this);
17449         this.expand(deep, anim, callback);
17450     },
17451     
17452     /**
17453      * Returns true if this node has been loaded
17454      * @return {Boolean}
17455      */
17456     isLoaded : function(){
17457         return this.loaded;
17458     },
17459     
17460     hasChildNodes : function(){
17461         if(!this.isLeaf() && !this.loaded){
17462             return true;
17463         }else{
17464             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17465         }
17466     },
17467
17468     /**
17469      * Trigger a reload for this node
17470      * @param {Function} callback
17471      */
17472     reload : function(callback){
17473         this.collapse(false, false);
17474         while(this.firstChild){
17475             this.removeChild(this.firstChild);
17476         }
17477         this.childrenRendered = false;
17478         this.loaded = false;
17479         if(this.isHiddenRoot()){
17480             this.expanded = false;
17481         }
17482         this.expand(false, false, callback);
17483     }
17484 });/*
17485  * Based on:
17486  * Ext JS Library 1.1.1
17487  * Copyright(c) 2006-2007, Ext JS, LLC.
17488  *
17489  * Originally Released Under LGPL - original licence link has changed is not relivant.
17490  *
17491  * Fork - LGPL
17492  * <script type="text/javascript">
17493  */
17494  
17495 /**
17496  * @class Roo.tree.TreeNodeUI
17497  * @constructor
17498  * @param {Object} node The node to render
17499  * The TreeNode UI implementation is separate from the
17500  * tree implementation. Unless you are customizing the tree UI,
17501  * you should never have to use this directly.
17502  */
17503 Roo.tree.TreeNodeUI = function(node){
17504     this.node = node;
17505     this.rendered = false;
17506     this.animating = false;
17507     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17508 };
17509
17510 Roo.tree.TreeNodeUI.prototype = {
17511     removeChild : function(node){
17512         if(this.rendered){
17513             this.ctNode.removeChild(node.ui.getEl());
17514         }
17515     },
17516
17517     beforeLoad : function(){
17518          this.addClass("x-tree-node-loading");
17519     },
17520
17521     afterLoad : function(){
17522          this.removeClass("x-tree-node-loading");
17523     },
17524
17525     onTextChange : function(node, text, oldText){
17526         if(this.rendered){
17527             this.textNode.innerHTML = text;
17528         }
17529     },
17530
17531     onDisableChange : function(node, state){
17532         this.disabled = state;
17533         if(state){
17534             this.addClass("x-tree-node-disabled");
17535         }else{
17536             this.removeClass("x-tree-node-disabled");
17537         }
17538     },
17539
17540     onSelectedChange : function(state){
17541         if(state){
17542             this.focus();
17543             this.addClass("x-tree-selected");
17544         }else{
17545             //this.blur();
17546             this.removeClass("x-tree-selected");
17547         }
17548     },
17549
17550     onMove : function(tree, node, oldParent, newParent, index, refNode){
17551         this.childIndent = null;
17552         if(this.rendered){
17553             var targetNode = newParent.ui.getContainer();
17554             if(!targetNode){//target not rendered
17555                 this.holder = document.createElement("div");
17556                 this.holder.appendChild(this.wrap);
17557                 return;
17558             }
17559             var insertBefore = refNode ? refNode.ui.getEl() : null;
17560             if(insertBefore){
17561                 targetNode.insertBefore(this.wrap, insertBefore);
17562             }else{
17563                 targetNode.appendChild(this.wrap);
17564             }
17565             this.node.renderIndent(true);
17566         }
17567     },
17568
17569     addClass : function(cls){
17570         if(this.elNode){
17571             Roo.fly(this.elNode).addClass(cls);
17572         }
17573     },
17574
17575     removeClass : function(cls){
17576         if(this.elNode){
17577             Roo.fly(this.elNode).removeClass(cls);
17578         }
17579     },
17580
17581     remove : function(){
17582         if(this.rendered){
17583             this.holder = document.createElement("div");
17584             this.holder.appendChild(this.wrap);
17585         }
17586     },
17587
17588     fireEvent : function(){
17589         return this.node.fireEvent.apply(this.node, arguments);
17590     },
17591
17592     initEvents : function(){
17593         this.node.on("move", this.onMove, this);
17594         var E = Roo.EventManager;
17595         var a = this.anchor;
17596
17597         var el = Roo.fly(a, '_treeui');
17598
17599         if(Roo.isOpera){ // opera render bug ignores the CSS
17600             el.setStyle("text-decoration", "none");
17601         }
17602
17603         el.on("click", this.onClick, this);
17604         el.on("dblclick", this.onDblClick, this);
17605
17606         if(this.checkbox){
17607             Roo.EventManager.on(this.checkbox,
17608                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17609         }
17610
17611         el.on("contextmenu", this.onContextMenu, this);
17612
17613         var icon = Roo.fly(this.iconNode);
17614         icon.on("click", this.onClick, this);
17615         icon.on("dblclick", this.onDblClick, this);
17616         icon.on("contextmenu", this.onContextMenu, this);
17617         E.on(this.ecNode, "click", this.ecClick, this, true);
17618
17619         if(this.node.disabled){
17620             this.addClass("x-tree-node-disabled");
17621         }
17622         if(this.node.hidden){
17623             this.addClass("x-tree-node-disabled");
17624         }
17625         var ot = this.node.getOwnerTree();
17626         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17627         if(dd && (!this.node.isRoot || ot.rootVisible)){
17628             Roo.dd.Registry.register(this.elNode, {
17629                 node: this.node,
17630                 handles: this.getDDHandles(),
17631                 isHandle: false
17632             });
17633         }
17634     },
17635
17636     getDDHandles : function(){
17637         return [this.iconNode, this.textNode];
17638     },
17639
17640     hide : function(){
17641         if(this.rendered){
17642             this.wrap.style.display = "none";
17643         }
17644     },
17645
17646     show : function(){
17647         if(this.rendered){
17648             this.wrap.style.display = "";
17649         }
17650     },
17651
17652     onContextMenu : function(e){
17653         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17654             e.preventDefault();
17655             this.focus();
17656             this.fireEvent("contextmenu", this.node, e);
17657         }
17658     },
17659
17660     onClick : function(e){
17661         if(this.dropping){
17662             e.stopEvent();
17663             return;
17664         }
17665         if(this.fireEvent("beforeclick", this.node, e) !== false){
17666             if(!this.disabled && this.node.attributes.href){
17667                 this.fireEvent("click", this.node, e);
17668                 return;
17669             }
17670             e.preventDefault();
17671             if(this.disabled){
17672                 return;
17673             }
17674
17675             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17676                 this.node.toggle();
17677             }
17678
17679             this.fireEvent("click", this.node, e);
17680         }else{
17681             e.stopEvent();
17682         }
17683     },
17684
17685     onDblClick : function(e){
17686         e.preventDefault();
17687         if(this.disabled){
17688             return;
17689         }
17690         if(this.checkbox){
17691             this.toggleCheck();
17692         }
17693         if(!this.animating && this.node.hasChildNodes()){
17694             this.node.toggle();
17695         }
17696         this.fireEvent("dblclick", this.node, e);
17697     },
17698
17699     onCheckChange : function(){
17700         var checked = this.checkbox.checked;
17701         this.node.attributes.checked = checked;
17702         this.fireEvent('checkchange', this.node, checked);
17703     },
17704
17705     ecClick : function(e){
17706         if(!this.animating && this.node.hasChildNodes()){
17707             this.node.toggle();
17708         }
17709     },
17710
17711     startDrop : function(){
17712         this.dropping = true;
17713     },
17714
17715     // delayed drop so the click event doesn't get fired on a drop
17716     endDrop : function(){
17717        setTimeout(function(){
17718            this.dropping = false;
17719        }.createDelegate(this), 50);
17720     },
17721
17722     expand : function(){
17723         this.updateExpandIcon();
17724         this.ctNode.style.display = "";
17725     },
17726
17727     focus : function(){
17728         if(!this.node.preventHScroll){
17729             try{this.anchor.focus();
17730             }catch(e){}
17731         }else if(!Roo.isIE){
17732             try{
17733                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17734                 var l = noscroll.scrollLeft;
17735                 this.anchor.focus();
17736                 noscroll.scrollLeft = l;
17737             }catch(e){}
17738         }
17739     },
17740
17741     toggleCheck : function(value){
17742         var cb = this.checkbox;
17743         if(cb){
17744             cb.checked = (value === undefined ? !cb.checked : value);
17745         }
17746     },
17747
17748     blur : function(){
17749         try{
17750             this.anchor.blur();
17751         }catch(e){}
17752     },
17753
17754     animExpand : function(callback){
17755         var ct = Roo.get(this.ctNode);
17756         ct.stopFx();
17757         if(!this.node.hasChildNodes()){
17758             this.updateExpandIcon();
17759             this.ctNode.style.display = "";
17760             Roo.callback(callback);
17761             return;
17762         }
17763         this.animating = true;
17764         this.updateExpandIcon();
17765
17766         ct.slideIn('t', {
17767            callback : function(){
17768                this.animating = false;
17769                Roo.callback(callback);
17770             },
17771             scope: this,
17772             duration: this.node.ownerTree.duration || .25
17773         });
17774     },
17775
17776     highlight : function(){
17777         var tree = this.node.getOwnerTree();
17778         Roo.fly(this.wrap).highlight(
17779             tree.hlColor || "C3DAF9",
17780             {endColor: tree.hlBaseColor}
17781         );
17782     },
17783
17784     collapse : function(){
17785         this.updateExpandIcon();
17786         this.ctNode.style.display = "none";
17787     },
17788
17789     animCollapse : function(callback){
17790         var ct = Roo.get(this.ctNode);
17791         ct.enableDisplayMode('block');
17792         ct.stopFx();
17793
17794         this.animating = true;
17795         this.updateExpandIcon();
17796
17797         ct.slideOut('t', {
17798             callback : function(){
17799                this.animating = false;
17800                Roo.callback(callback);
17801             },
17802             scope: this,
17803             duration: this.node.ownerTree.duration || .25
17804         });
17805     },
17806
17807     getContainer : function(){
17808         return this.ctNode;
17809     },
17810
17811     getEl : function(){
17812         return this.wrap;
17813     },
17814
17815     appendDDGhost : function(ghostNode){
17816         ghostNode.appendChild(this.elNode.cloneNode(true));
17817     },
17818
17819     getDDRepairXY : function(){
17820         return Roo.lib.Dom.getXY(this.iconNode);
17821     },
17822
17823     onRender : function(){
17824         this.render();
17825     },
17826
17827     render : function(bulkRender){
17828         var n = this.node, a = n.attributes;
17829         var targetNode = n.parentNode ?
17830               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17831
17832         if(!this.rendered){
17833             this.rendered = true;
17834
17835             this.renderElements(n, a, targetNode, bulkRender);
17836
17837             if(a.qtip){
17838                if(this.textNode.setAttributeNS){
17839                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17840                    if(a.qtipTitle){
17841                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17842                    }
17843                }else{
17844                    this.textNode.setAttribute("ext:qtip", a.qtip);
17845                    if(a.qtipTitle){
17846                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17847                    }
17848                }
17849             }else if(a.qtipCfg){
17850                 a.qtipCfg.target = Roo.id(this.textNode);
17851                 Roo.QuickTips.register(a.qtipCfg);
17852             }
17853             this.initEvents();
17854             if(!this.node.expanded){
17855                 this.updateExpandIcon();
17856             }
17857         }else{
17858             if(bulkRender === true) {
17859                 targetNode.appendChild(this.wrap);
17860             }
17861         }
17862     },
17863
17864     renderElements : function(n, a, targetNode, bulkRender){
17865         // add some indent caching, this helps performance when rendering a large tree
17866         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17867         var t = n.getOwnerTree();
17868         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17869         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17870         var cb = typeof a.checked == 'boolean';
17871         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17872         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17873             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17874             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17875             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17876             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17877             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17878              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17879                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17880             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17881             "</li>"];
17882
17883         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17884             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17885                                 n.nextSibling.ui.getEl(), buf.join(""));
17886         }else{
17887             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17888         }
17889
17890         this.elNode = this.wrap.childNodes[0];
17891         this.ctNode = this.wrap.childNodes[1];
17892         var cs = this.elNode.childNodes;
17893         this.indentNode = cs[0];
17894         this.ecNode = cs[1];
17895         this.iconNode = cs[2];
17896         var index = 3;
17897         if(cb){
17898             this.checkbox = cs[3];
17899             index++;
17900         }
17901         this.anchor = cs[index];
17902         this.textNode = cs[index].firstChild;
17903     },
17904
17905     getAnchor : function(){
17906         return this.anchor;
17907     },
17908
17909     getTextEl : function(){
17910         return this.textNode;
17911     },
17912
17913     getIconEl : function(){
17914         return this.iconNode;
17915     },
17916
17917     isChecked : function(){
17918         return this.checkbox ? this.checkbox.checked : false;
17919     },
17920
17921     updateExpandIcon : function(){
17922         if(this.rendered){
17923             var n = this.node, c1, c2;
17924             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17925             var hasChild = n.hasChildNodes();
17926             if(hasChild){
17927                 if(n.expanded){
17928                     cls += "-minus";
17929                     c1 = "x-tree-node-collapsed";
17930                     c2 = "x-tree-node-expanded";
17931                 }else{
17932                     cls += "-plus";
17933                     c1 = "x-tree-node-expanded";
17934                     c2 = "x-tree-node-collapsed";
17935                 }
17936                 if(this.wasLeaf){
17937                     this.removeClass("x-tree-node-leaf");
17938                     this.wasLeaf = false;
17939                 }
17940                 if(this.c1 != c1 || this.c2 != c2){
17941                     Roo.fly(this.elNode).replaceClass(c1, c2);
17942                     this.c1 = c1; this.c2 = c2;
17943                 }
17944             }else{
17945                 if(!this.wasLeaf){
17946                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17947                     delete this.c1;
17948                     delete this.c2;
17949                     this.wasLeaf = true;
17950                 }
17951             }
17952             var ecc = "x-tree-ec-icon "+cls;
17953             if(this.ecc != ecc){
17954                 this.ecNode.className = ecc;
17955                 this.ecc = ecc;
17956             }
17957         }
17958     },
17959
17960     getChildIndent : function(){
17961         if(!this.childIndent){
17962             var buf = [];
17963             var p = this.node;
17964             while(p){
17965                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17966                     if(!p.isLast()) {
17967                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17968                     } else {
17969                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17970                     }
17971                 }
17972                 p = p.parentNode;
17973             }
17974             this.childIndent = buf.join("");
17975         }
17976         return this.childIndent;
17977     },
17978
17979     renderIndent : function(){
17980         if(this.rendered){
17981             var indent = "";
17982             var p = this.node.parentNode;
17983             if(p){
17984                 indent = p.ui.getChildIndent();
17985             }
17986             if(this.indentMarkup != indent){ // don't rerender if not required
17987                 this.indentNode.innerHTML = indent;
17988                 this.indentMarkup = indent;
17989             }
17990             this.updateExpandIcon();
17991         }
17992     }
17993 };
17994
17995 Roo.tree.RootTreeNodeUI = function(){
17996     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17997 };
17998 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17999     render : function(){
18000         if(!this.rendered){
18001             var targetNode = this.node.ownerTree.innerCt.dom;
18002             this.node.expanded = true;
18003             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18004             this.wrap = this.ctNode = targetNode.firstChild;
18005         }
18006     },
18007     collapse : function(){
18008     },
18009     expand : function(){
18010     }
18011 });/*
18012  * Based on:
18013  * Ext JS Library 1.1.1
18014  * Copyright(c) 2006-2007, Ext JS, LLC.
18015  *
18016  * Originally Released Under LGPL - original licence link has changed is not relivant.
18017  *
18018  * Fork - LGPL
18019  * <script type="text/javascript">
18020  */
18021 /**
18022  * @class Roo.tree.TreeLoader
18023  * @extends Roo.util.Observable
18024  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18025  * nodes from a specified URL. The response must be a javascript Array definition
18026  * who's elements are node definition objects. eg:
18027  * <pre><code>
18028    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18029     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18030 </code></pre>
18031  * <br><br>
18032  * A server request is sent, and child nodes are loaded only when a node is expanded.
18033  * The loading node's id is passed to the server under the parameter name "node" to
18034  * enable the server to produce the correct child nodes.
18035  * <br><br>
18036  * To pass extra parameters, an event handler may be attached to the "beforeload"
18037  * event, and the parameters specified in the TreeLoader's baseParams property:
18038  * <pre><code>
18039     myTreeLoader.on("beforeload", function(treeLoader, node) {
18040         this.baseParams.category = node.attributes.category;
18041     }, this);
18042 </code></pre><
18043  * This would pass an HTTP parameter called "category" to the server containing
18044  * the value of the Node's "category" attribute.
18045  * @constructor
18046  * Creates a new Treeloader.
18047  * @param {Object} config A config object containing config properties.
18048  */
18049 Roo.tree.TreeLoader = function(config){
18050     this.baseParams = {};
18051     this.requestMethod = "POST";
18052     Roo.apply(this, config);
18053
18054     this.addEvents({
18055     
18056         /**
18057          * @event beforeload
18058          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18059          * @param {Object} This TreeLoader object.
18060          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18061          * @param {Object} callback The callback function specified in the {@link #load} call.
18062          */
18063         beforeload : true,
18064         /**
18065          * @event load
18066          * Fires when the node has been successfuly loaded.
18067          * @param {Object} This TreeLoader object.
18068          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18069          * @param {Object} response The response object containing the data from the server.
18070          */
18071         load : true,
18072         /**
18073          * @event loadexception
18074          * Fires if the network request failed.
18075          * @param {Object} This TreeLoader object.
18076          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18077          * @param {Object} response The response object containing the data from the server.
18078          */
18079         loadexception : true,
18080         /**
18081          * @event create
18082          * Fires before a node is created, enabling you to return custom Node types 
18083          * @param {Object} This TreeLoader object.
18084          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18085          */
18086         create : true
18087     });
18088
18089     Roo.tree.TreeLoader.superclass.constructor.call(this);
18090 };
18091
18092 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18093     /**
18094     * @cfg {String} dataUrl The URL from which to request a Json string which
18095     * specifies an array of node definition object representing the child nodes
18096     * to be loaded.
18097     */
18098     /**
18099     * @cfg {Object} baseParams (optional) An object containing properties which
18100     * specify HTTP parameters to be passed to each request for child nodes.
18101     */
18102     /**
18103     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18104     * created by this loader. If the attributes sent by the server have an attribute in this object,
18105     * they take priority.
18106     */
18107     /**
18108     * @cfg {Object} uiProviders (optional) An object containing properties which
18109     * 
18110     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18111     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18112     * <i>uiProvider</i> attribute of a returned child node is a string rather
18113     * than a reference to a TreeNodeUI implementation, this that string value
18114     * is used as a property name in the uiProviders object. You can define the provider named
18115     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18116     */
18117     uiProviders : {},
18118
18119     /**
18120     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18121     * child nodes before loading.
18122     */
18123     clearOnLoad : true,
18124
18125     /**
18126     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18127     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18128     * Grid query { data : [ .....] }
18129     */
18130     
18131     root : false,
18132      /**
18133     * @cfg {String} queryParam (optional) 
18134     * Name of the query as it will be passed on the querystring (defaults to 'node')
18135     * eg. the request will be ?node=[id]
18136     */
18137     
18138     
18139     queryParam: false,
18140     
18141     /**
18142      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18143      * This is called automatically when a node is expanded, but may be used to reload
18144      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18145      * @param {Roo.tree.TreeNode} node
18146      * @param {Function} callback
18147      */
18148     load : function(node, callback){
18149         if(this.clearOnLoad){
18150             while(node.firstChild){
18151                 node.removeChild(node.firstChild);
18152             }
18153         }
18154         if(node.attributes.children){ // preloaded json children
18155             var cs = node.attributes.children;
18156             for(var i = 0, len = cs.length; i < len; i++){
18157                 node.appendChild(this.createNode(cs[i]));
18158             }
18159             if(typeof callback == "function"){
18160                 callback();
18161             }
18162         }else if(this.dataUrl){
18163             this.requestData(node, callback);
18164         }
18165     },
18166
18167     getParams: function(node){
18168         var buf = [], bp = this.baseParams;
18169         for(var key in bp){
18170             if(typeof bp[key] != "function"){
18171                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18172             }
18173         }
18174         var n = this.queryParam === false ? 'node' : this.queryParam;
18175         buf.push(n + "=", encodeURIComponent(node.id));
18176         return buf.join("");
18177     },
18178
18179     requestData : function(node, callback){
18180         if(this.fireEvent("beforeload", this, node, callback) !== false){
18181             this.transId = Roo.Ajax.request({
18182                 method:this.requestMethod,
18183                 url: this.dataUrl||this.url,
18184                 success: this.handleResponse,
18185                 failure: this.handleFailure,
18186                 scope: this,
18187                 argument: {callback: callback, node: node},
18188                 params: this.getParams(node)
18189             });
18190         }else{
18191             // if the load is cancelled, make sure we notify
18192             // the node that we are done
18193             if(typeof callback == "function"){
18194                 callback();
18195             }
18196         }
18197     },
18198
18199     isLoading : function(){
18200         return this.transId ? true : false;
18201     },
18202
18203     abort : function(){
18204         if(this.isLoading()){
18205             Roo.Ajax.abort(this.transId);
18206         }
18207     },
18208
18209     // private
18210     createNode : function(attr){
18211         // apply baseAttrs, nice idea Corey!
18212         if(this.baseAttrs){
18213             Roo.applyIf(attr, this.baseAttrs);
18214         }
18215         if(this.applyLoader !== false){
18216             attr.loader = this;
18217         }
18218         // uiProvider = depreciated..
18219         
18220         if(typeof(attr.uiProvider) == 'string'){
18221            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18222                 /**  eval:var:attr */ eval(attr.uiProvider);
18223         }
18224         if(typeof(this.uiProviders['default']) != 'undefined') {
18225             attr.uiProvider = this.uiProviders['default'];
18226         }
18227         
18228         this.fireEvent('create', this, attr);
18229         
18230         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18231         return(attr.leaf ?
18232                         new Roo.tree.TreeNode(attr) :
18233                         new Roo.tree.AsyncTreeNode(attr));
18234     },
18235
18236     processResponse : function(response, node, callback){
18237         var json = response.responseText;
18238         try {
18239             
18240             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18241             if (this.root !== false) {
18242                 o = o[this.root];
18243             }
18244             
18245             for(var i = 0, len = o.length; i < len; i++){
18246                 var n = this.createNode(o[i]);
18247                 if(n){
18248                     node.appendChild(n);
18249                 }
18250             }
18251             if(typeof callback == "function"){
18252                 callback(this, node);
18253             }
18254         }catch(e){
18255             this.handleFailure(response);
18256         }
18257     },
18258
18259     handleResponse : function(response){
18260         this.transId = false;
18261         var a = response.argument;
18262         this.processResponse(response, a.node, a.callback);
18263         this.fireEvent("load", this, a.node, response);
18264     },
18265
18266     handleFailure : function(response){
18267         this.transId = false;
18268         var a = response.argument;
18269         this.fireEvent("loadexception", this, a.node, response);
18270         if(typeof a.callback == "function"){
18271             a.callback(this, a.node);
18272         }
18273     }
18274 });/*
18275  * Based on:
18276  * Ext JS Library 1.1.1
18277  * Copyright(c) 2006-2007, Ext JS, LLC.
18278  *
18279  * Originally Released Under LGPL - original licence link has changed is not relivant.
18280  *
18281  * Fork - LGPL
18282  * <script type="text/javascript">
18283  */
18284
18285 /**
18286 * @class Roo.tree.TreeFilter
18287 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18288 * @param {TreePanel} tree
18289 * @param {Object} config (optional)
18290  */
18291 Roo.tree.TreeFilter = function(tree, config){
18292     this.tree = tree;
18293     this.filtered = {};
18294     Roo.apply(this, config);
18295 };
18296
18297 Roo.tree.TreeFilter.prototype = {
18298     clearBlank:false,
18299     reverse:false,
18300     autoClear:false,
18301     remove:false,
18302
18303      /**
18304      * Filter the data by a specific attribute.
18305      * @param {String/RegExp} value Either string that the attribute value
18306      * should start with or a RegExp to test against the attribute
18307      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18308      * @param {TreeNode} startNode (optional) The node to start the filter at.
18309      */
18310     filter : function(value, attr, startNode){
18311         attr = attr || "text";
18312         var f;
18313         if(typeof value == "string"){
18314             var vlen = value.length;
18315             // auto clear empty filter
18316             if(vlen == 0 && this.clearBlank){
18317                 this.clear();
18318                 return;
18319             }
18320             value = value.toLowerCase();
18321             f = function(n){
18322                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18323             };
18324         }else if(value.exec){ // regex?
18325             f = function(n){
18326                 return value.test(n.attributes[attr]);
18327             };
18328         }else{
18329             throw 'Illegal filter type, must be string or regex';
18330         }
18331         this.filterBy(f, null, startNode);
18332         },
18333
18334     /**
18335      * Filter by a function. The passed function will be called with each
18336      * node in the tree (or from the startNode). If the function returns true, the node is kept
18337      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18338      * @param {Function} fn The filter function
18339      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18340      */
18341     filterBy : function(fn, scope, startNode){
18342         startNode = startNode || this.tree.root;
18343         if(this.autoClear){
18344             this.clear();
18345         }
18346         var af = this.filtered, rv = this.reverse;
18347         var f = function(n){
18348             if(n == startNode){
18349                 return true;
18350             }
18351             if(af[n.id]){
18352                 return false;
18353             }
18354             var m = fn.call(scope || n, n);
18355             if(!m || rv){
18356                 af[n.id] = n;
18357                 n.ui.hide();
18358                 return false;
18359             }
18360             return true;
18361         };
18362         startNode.cascade(f);
18363         if(this.remove){
18364            for(var id in af){
18365                if(typeof id != "function"){
18366                    var n = af[id];
18367                    if(n && n.parentNode){
18368                        n.parentNode.removeChild(n);
18369                    }
18370                }
18371            }
18372         }
18373     },
18374
18375     /**
18376      * Clears the current filter. Note: with the "remove" option
18377      * set a filter cannot be cleared.
18378      */
18379     clear : function(){
18380         var t = this.tree;
18381         var af = this.filtered;
18382         for(var id in af){
18383             if(typeof id != "function"){
18384                 var n = af[id];
18385                 if(n){
18386                     n.ui.show();
18387                 }
18388             }
18389         }
18390         this.filtered = {};
18391     }
18392 };
18393 /*
18394  * Based on:
18395  * Ext JS Library 1.1.1
18396  * Copyright(c) 2006-2007, Ext JS, LLC.
18397  *
18398  * Originally Released Under LGPL - original licence link has changed is not relivant.
18399  *
18400  * Fork - LGPL
18401  * <script type="text/javascript">
18402  */
18403  
18404
18405 /**
18406  * @class Roo.tree.TreeSorter
18407  * Provides sorting of nodes in a TreePanel
18408  * 
18409  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18410  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18411  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18412  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18413  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18414  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18415  * @constructor
18416  * @param {TreePanel} tree
18417  * @param {Object} config
18418  */
18419 Roo.tree.TreeSorter = function(tree, config){
18420     Roo.apply(this, config);
18421     tree.on("beforechildrenrendered", this.doSort, this);
18422     tree.on("append", this.updateSort, this);
18423     tree.on("insert", this.updateSort, this);
18424     
18425     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18426     var p = this.property || "text";
18427     var sortType = this.sortType;
18428     var fs = this.folderSort;
18429     var cs = this.caseSensitive === true;
18430     var leafAttr = this.leafAttr || 'leaf';
18431
18432     this.sortFn = function(n1, n2){
18433         if(fs){
18434             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18435                 return 1;
18436             }
18437             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18438                 return -1;
18439             }
18440         }
18441         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18442         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18443         if(v1 < v2){
18444                         return dsc ? +1 : -1;
18445                 }else if(v1 > v2){
18446                         return dsc ? -1 : +1;
18447         }else{
18448                 return 0;
18449         }
18450     };
18451 };
18452
18453 Roo.tree.TreeSorter.prototype = {
18454     doSort : function(node){
18455         node.sort(this.sortFn);
18456     },
18457     
18458     compareNodes : function(n1, n2){
18459         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18460     },
18461     
18462     updateSort : function(tree, node){
18463         if(node.childrenRendered){
18464             this.doSort.defer(1, this, [node]);
18465         }
18466     }
18467 };/*
18468  * Based on:
18469  * Ext JS Library 1.1.1
18470  * Copyright(c) 2006-2007, Ext JS, LLC.
18471  *
18472  * Originally Released Under LGPL - original licence link has changed is not relivant.
18473  *
18474  * Fork - LGPL
18475  * <script type="text/javascript">
18476  */
18477
18478 if(Roo.dd.DropZone){
18479     
18480 Roo.tree.TreeDropZone = function(tree, config){
18481     this.allowParentInsert = false;
18482     this.allowContainerDrop = false;
18483     this.appendOnly = false;
18484     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18485     this.tree = tree;
18486     this.lastInsertClass = "x-tree-no-status";
18487     this.dragOverData = {};
18488 };
18489
18490 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18491     ddGroup : "TreeDD",
18492     
18493     expandDelay : 1000,
18494     
18495     expandNode : function(node){
18496         if(node.hasChildNodes() && !node.isExpanded()){
18497             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18498         }
18499     },
18500     
18501     queueExpand : function(node){
18502         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18503     },
18504     
18505     cancelExpand : function(){
18506         if(this.expandProcId){
18507             clearTimeout(this.expandProcId);
18508             this.expandProcId = false;
18509         }
18510     },
18511     
18512     isValidDropPoint : function(n, pt, dd, e, data){
18513         if(!n || !data){ return false; }
18514         var targetNode = n.node;
18515         var dropNode = data.node;
18516         // default drop rules
18517         if(!(targetNode && targetNode.isTarget && pt)){
18518             return false;
18519         }
18520         if(pt == "append" && targetNode.allowChildren === false){
18521             return false;
18522         }
18523         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18524             return false;
18525         }
18526         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18527             return false;
18528         }
18529         // reuse the object
18530         var overEvent = this.dragOverData;
18531         overEvent.tree = this.tree;
18532         overEvent.target = targetNode;
18533         overEvent.data = data;
18534         overEvent.point = pt;
18535         overEvent.source = dd;
18536         overEvent.rawEvent = e;
18537         overEvent.dropNode = dropNode;
18538         overEvent.cancel = false;  
18539         var result = this.tree.fireEvent("nodedragover", overEvent);
18540         return overEvent.cancel === false && result !== false;
18541     },
18542     
18543     getDropPoint : function(e, n, dd){
18544         var tn = n.node;
18545         if(tn.isRoot){
18546             return tn.allowChildren !== false ? "append" : false; // always append for root
18547         }
18548         var dragEl = n.ddel;
18549         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18550         var y = Roo.lib.Event.getPageY(e);
18551         var noAppend = tn.allowChildren === false || tn.isLeaf();
18552         if(this.appendOnly || tn.parentNode.allowChildren === false){
18553             return noAppend ? false : "append";
18554         }
18555         var noBelow = false;
18556         if(!this.allowParentInsert){
18557             noBelow = tn.hasChildNodes() && tn.isExpanded();
18558         }
18559         var q = (b - t) / (noAppend ? 2 : 3);
18560         if(y >= t && y < (t + q)){
18561             return "above";
18562         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18563             return "below";
18564         }else{
18565             return "append";
18566         }
18567     },
18568     
18569     onNodeEnter : function(n, dd, e, data){
18570         this.cancelExpand();
18571     },
18572     
18573     onNodeOver : function(n, dd, e, data){
18574         var pt = this.getDropPoint(e, n, dd);
18575         var node = n.node;
18576         
18577         // auto node expand check
18578         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18579             this.queueExpand(node);
18580         }else if(pt != "append"){
18581             this.cancelExpand();
18582         }
18583         
18584         // set the insert point style on the target node
18585         var returnCls = this.dropNotAllowed;
18586         if(this.isValidDropPoint(n, pt, dd, e, data)){
18587            if(pt){
18588                var el = n.ddel;
18589                var cls;
18590                if(pt == "above"){
18591                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18592                    cls = "x-tree-drag-insert-above";
18593                }else if(pt == "below"){
18594                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18595                    cls = "x-tree-drag-insert-below";
18596                }else{
18597                    returnCls = "x-tree-drop-ok-append";
18598                    cls = "x-tree-drag-append";
18599                }
18600                if(this.lastInsertClass != cls){
18601                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18602                    this.lastInsertClass = cls;
18603                }
18604            }
18605        }
18606        return returnCls;
18607     },
18608     
18609     onNodeOut : function(n, dd, e, data){
18610         this.cancelExpand();
18611         this.removeDropIndicators(n);
18612     },
18613     
18614     onNodeDrop : function(n, dd, e, data){
18615         var point = this.getDropPoint(e, n, dd);
18616         var targetNode = n.node;
18617         targetNode.ui.startDrop();
18618         if(!this.isValidDropPoint(n, point, dd, e, data)){
18619             targetNode.ui.endDrop();
18620             return false;
18621         }
18622         // first try to find the drop node
18623         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18624         var dropEvent = {
18625             tree : this.tree,
18626             target: targetNode,
18627             data: data,
18628             point: point,
18629             source: dd,
18630             rawEvent: e,
18631             dropNode: dropNode,
18632             cancel: !dropNode   
18633         };
18634         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18635         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18636             targetNode.ui.endDrop();
18637             return false;
18638         }
18639         // allow target changing
18640         targetNode = dropEvent.target;
18641         if(point == "append" && !targetNode.isExpanded()){
18642             targetNode.expand(false, null, function(){
18643                 this.completeDrop(dropEvent);
18644             }.createDelegate(this));
18645         }else{
18646             this.completeDrop(dropEvent);
18647         }
18648         return true;
18649     },
18650     
18651     completeDrop : function(de){
18652         var ns = de.dropNode, p = de.point, t = de.target;
18653         if(!(ns instanceof Array)){
18654             ns = [ns];
18655         }
18656         var n;
18657         for(var i = 0, len = ns.length; i < len; i++){
18658             n = ns[i];
18659             if(p == "above"){
18660                 t.parentNode.insertBefore(n, t);
18661             }else if(p == "below"){
18662                 t.parentNode.insertBefore(n, t.nextSibling);
18663             }else{
18664                 t.appendChild(n);
18665             }
18666         }
18667         n.ui.focus();
18668         if(this.tree.hlDrop){
18669             n.ui.highlight();
18670         }
18671         t.ui.endDrop();
18672         this.tree.fireEvent("nodedrop", de);
18673     },
18674     
18675     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18676         if(this.tree.hlDrop){
18677             dropNode.ui.focus();
18678             dropNode.ui.highlight();
18679         }
18680         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18681     },
18682     
18683     getTree : function(){
18684         return this.tree;
18685     },
18686     
18687     removeDropIndicators : function(n){
18688         if(n && n.ddel){
18689             var el = n.ddel;
18690             Roo.fly(el).removeClass([
18691                     "x-tree-drag-insert-above",
18692                     "x-tree-drag-insert-below",
18693                     "x-tree-drag-append"]);
18694             this.lastInsertClass = "_noclass";
18695         }
18696     },
18697     
18698     beforeDragDrop : function(target, e, id){
18699         this.cancelExpand();
18700         return true;
18701     },
18702     
18703     afterRepair : function(data){
18704         if(data && Roo.enableFx){
18705             data.node.ui.highlight();
18706         }
18707         this.hideProxy();
18708     }    
18709 });
18710
18711 }/*
18712  * Based on:
18713  * Ext JS Library 1.1.1
18714  * Copyright(c) 2006-2007, Ext JS, LLC.
18715  *
18716  * Originally Released Under LGPL - original licence link has changed is not relivant.
18717  *
18718  * Fork - LGPL
18719  * <script type="text/javascript">
18720  */
18721  
18722
18723 if(Roo.dd.DragZone){
18724 Roo.tree.TreeDragZone = function(tree, config){
18725     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18726     this.tree = tree;
18727 };
18728
18729 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18730     ddGroup : "TreeDD",
18731     
18732     onBeforeDrag : function(data, e){
18733         var n = data.node;
18734         return n && n.draggable && !n.disabled;
18735     },
18736     
18737     onInitDrag : function(e){
18738         var data = this.dragData;
18739         this.tree.getSelectionModel().select(data.node);
18740         this.proxy.update("");
18741         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18742         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18743     },
18744     
18745     getRepairXY : function(e, data){
18746         return data.node.ui.getDDRepairXY();
18747     },
18748     
18749     onEndDrag : function(data, e){
18750         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18751     },
18752     
18753     onValidDrop : function(dd, e, id){
18754         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18755         this.hideProxy();
18756     },
18757     
18758     beforeInvalidDrop : function(e, id){
18759         // this scrolls the original position back into view
18760         var sm = this.tree.getSelectionModel();
18761         sm.clearSelections();
18762         sm.select(this.dragData.node);
18763     }
18764 });
18765 }/*
18766  * Based on:
18767  * Ext JS Library 1.1.1
18768  * Copyright(c) 2006-2007, Ext JS, LLC.
18769  *
18770  * Originally Released Under LGPL - original licence link has changed is not relivant.
18771  *
18772  * Fork - LGPL
18773  * <script type="text/javascript">
18774  */
18775 /**
18776  * @class Roo.tree.TreeEditor
18777  * @extends Roo.Editor
18778  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18779  * as the editor field.
18780  * @constructor
18781  * @param {TreePanel} tree
18782  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18783  */
18784 Roo.tree.TreeEditor = function(tree, config){
18785     config = config || {};
18786     var field = config.events ? config : new Roo.form.TextField(config);
18787     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18788
18789     this.tree = tree;
18790
18791     tree.on('beforeclick', this.beforeNodeClick, this);
18792     tree.getTreeEl().on('mousedown', this.hide, this);
18793     this.on('complete', this.updateNode, this);
18794     this.on('beforestartedit', this.fitToTree, this);
18795     this.on('startedit', this.bindScroll, this, {delay:10});
18796     this.on('specialkey', this.onSpecialKey, this);
18797 };
18798
18799 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18800     /**
18801      * @cfg {String} alignment
18802      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18803      */
18804     alignment: "l-l",
18805     // inherit
18806     autoSize: false,
18807     /**
18808      * @cfg {Boolean} hideEl
18809      * True to hide the bound element while the editor is displayed (defaults to false)
18810      */
18811     hideEl : false,
18812     /**
18813      * @cfg {String} cls
18814      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18815      */
18816     cls: "x-small-editor x-tree-editor",
18817     /**
18818      * @cfg {Boolean} shim
18819      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18820      */
18821     shim:false,
18822     // inherit
18823     shadow:"frame",
18824     /**
18825      * @cfg {Number} maxWidth
18826      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18827      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18828      * scroll and client offsets into account prior to each edit.
18829      */
18830     maxWidth: 250,
18831
18832     editDelay : 350,
18833
18834     // private
18835     fitToTree : function(ed, el){
18836         var td = this.tree.getTreeEl().dom, nd = el.dom;
18837         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18838             td.scrollLeft = nd.offsetLeft;
18839         }
18840         var w = Math.min(
18841                 this.maxWidth,
18842                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18843         this.setSize(w, '');
18844     },
18845
18846     // private
18847     triggerEdit : function(node){
18848         this.completeEdit();
18849         this.editNode = node;
18850         this.startEdit(node.ui.textNode, node.text);
18851     },
18852
18853     // private
18854     bindScroll : function(){
18855         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18856     },
18857
18858     // private
18859     beforeNodeClick : function(node, e){
18860         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18861         this.lastClick = new Date();
18862         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18863             e.stopEvent();
18864             this.triggerEdit(node);
18865             return false;
18866         }
18867     },
18868
18869     // private
18870     updateNode : function(ed, value){
18871         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18872         this.editNode.setText(value);
18873     },
18874
18875     // private
18876     onHide : function(){
18877         Roo.tree.TreeEditor.superclass.onHide.call(this);
18878         if(this.editNode){
18879             this.editNode.ui.focus();
18880         }
18881     },
18882
18883     // private
18884     onSpecialKey : function(field, e){
18885         var k = e.getKey();
18886         if(k == e.ESC){
18887             e.stopEvent();
18888             this.cancelEdit();
18889         }else if(k == e.ENTER && !e.hasModifier()){
18890             e.stopEvent();
18891             this.completeEdit();
18892         }
18893     }
18894 });//<Script type="text/javascript">
18895 /*
18896  * Based on:
18897  * Ext JS Library 1.1.1
18898  * Copyright(c) 2006-2007, Ext JS, LLC.
18899  *
18900  * Originally Released Under LGPL - original licence link has changed is not relivant.
18901  *
18902  * Fork - LGPL
18903  * <script type="text/javascript">
18904  */
18905  
18906 /**
18907  * Not documented??? - probably should be...
18908  */
18909
18910 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18911     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18912     
18913     renderElements : function(n, a, targetNode, bulkRender){
18914         //consel.log("renderElements?");
18915         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18916
18917         var t = n.getOwnerTree();
18918         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18919         
18920         var cols = t.columns;
18921         var bw = t.borderWidth;
18922         var c = cols[0];
18923         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18924          var cb = typeof a.checked == "boolean";
18925         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18926         var colcls = 'x-t-' + tid + '-c0';
18927         var buf = [
18928             '<li class="x-tree-node">',
18929             
18930                 
18931                 '<div class="x-tree-node-el ', a.cls,'">',
18932                     // extran...
18933                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18934                 
18935                 
18936                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18937                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18938                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18939                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18940                            (a.iconCls ? ' '+a.iconCls : ''),
18941                            '" unselectable="on" />',
18942                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18943                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18944                              
18945                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18946                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18947                             '<span unselectable="on" qtip="' + tx + '">',
18948                              tx,
18949                              '</span></a>' ,
18950                     '</div>',
18951                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18952                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18953                  ];
18954         
18955         for(var i = 1, len = cols.length; i < len; i++){
18956             c = cols[i];
18957             colcls = 'x-t-' + tid + '-c' +i;
18958             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18959             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18960                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18961                       "</div>");
18962          }
18963          
18964          buf.push(
18965             '</a>',
18966             '<div class="x-clear"></div></div>',
18967             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18968             "</li>");
18969         
18970         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18971             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18972                                 n.nextSibling.ui.getEl(), buf.join(""));
18973         }else{
18974             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18975         }
18976         var el = this.wrap.firstChild;
18977         this.elRow = el;
18978         this.elNode = el.firstChild;
18979         this.ranchor = el.childNodes[1];
18980         this.ctNode = this.wrap.childNodes[1];
18981         var cs = el.firstChild.childNodes;
18982         this.indentNode = cs[0];
18983         this.ecNode = cs[1];
18984         this.iconNode = cs[2];
18985         var index = 3;
18986         if(cb){
18987             this.checkbox = cs[3];
18988             index++;
18989         }
18990         this.anchor = cs[index];
18991         
18992         this.textNode = cs[index].firstChild;
18993         
18994         //el.on("click", this.onClick, this);
18995         //el.on("dblclick", this.onDblClick, this);
18996         
18997         
18998        // console.log(this);
18999     },
19000     initEvents : function(){
19001         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19002         
19003             
19004         var a = this.ranchor;
19005
19006         var el = Roo.get(a);
19007
19008         if(Roo.isOpera){ // opera render bug ignores the CSS
19009             el.setStyle("text-decoration", "none");
19010         }
19011
19012         el.on("click", this.onClick, this);
19013         el.on("dblclick", this.onDblClick, this);
19014         el.on("contextmenu", this.onContextMenu, this);
19015         
19016     },
19017     
19018     /*onSelectedChange : function(state){
19019         if(state){
19020             this.focus();
19021             this.addClass("x-tree-selected");
19022         }else{
19023             //this.blur();
19024             this.removeClass("x-tree-selected");
19025         }
19026     },*/
19027     addClass : function(cls){
19028         if(this.elRow){
19029             Roo.fly(this.elRow).addClass(cls);
19030         }
19031         
19032     },
19033     
19034     
19035     removeClass : function(cls){
19036         if(this.elRow){
19037             Roo.fly(this.elRow).removeClass(cls);
19038         }
19039     }
19040
19041     
19042     
19043 });//<Script type="text/javascript">
19044
19045 /*
19046  * Based on:
19047  * Ext JS Library 1.1.1
19048  * Copyright(c) 2006-2007, Ext JS, LLC.
19049  *
19050  * Originally Released Under LGPL - original licence link has changed is not relivant.
19051  *
19052  * Fork - LGPL
19053  * <script type="text/javascript">
19054  */
19055  
19056
19057 /**
19058  * @class Roo.tree.ColumnTree
19059  * @extends Roo.data.TreePanel
19060  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19061  * @cfg {int} borderWidth  compined right/left border allowance
19062  * @constructor
19063  * @param {String/HTMLElement/Element} el The container element
19064  * @param {Object} config
19065  */
19066 Roo.tree.ColumnTree =  function(el, config)
19067 {
19068    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19069    this.addEvents({
19070         /**
19071         * @event resize
19072         * Fire this event on a container when it resizes
19073         * @param {int} w Width
19074         * @param {int} h Height
19075         */
19076        "resize" : true
19077     });
19078     this.on('resize', this.onResize, this);
19079 };
19080
19081 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19082     //lines:false,
19083     
19084     
19085     borderWidth: Roo.isBorderBox ? 0 : 2, 
19086     headEls : false,
19087     
19088     render : function(){
19089         // add the header.....
19090        
19091         Roo.tree.ColumnTree.superclass.render.apply(this);
19092         
19093         this.el.addClass('x-column-tree');
19094         
19095         this.headers = this.el.createChild(
19096             {cls:'x-tree-headers'},this.innerCt.dom);
19097    
19098         var cols = this.columns, c;
19099         var totalWidth = 0;
19100         this.headEls = [];
19101         var  len = cols.length;
19102         for(var i = 0; i < len; i++){
19103              c = cols[i];
19104              totalWidth += c.width;
19105             this.headEls.push(this.headers.createChild({
19106                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19107                  cn: {
19108                      cls:'x-tree-hd-text',
19109                      html: c.header
19110                  },
19111                  style:'width:'+(c.width-this.borderWidth)+'px;'
19112              }));
19113         }
19114         this.headers.createChild({cls:'x-clear'});
19115         // prevent floats from wrapping when clipped
19116         this.headers.setWidth(totalWidth);
19117         //this.innerCt.setWidth(totalWidth);
19118         this.innerCt.setStyle({ overflow: 'auto' });
19119         this.onResize(this.width, this.height);
19120              
19121         
19122     },
19123     onResize : function(w,h)
19124     {
19125         this.height = h;
19126         this.width = w;
19127         // resize cols..
19128         this.innerCt.setWidth(this.width);
19129         this.innerCt.setHeight(this.height-20);
19130         
19131         // headers...
19132         var cols = this.columns, c;
19133         var totalWidth = 0;
19134         var expEl = false;
19135         var len = cols.length;
19136         for(var i = 0; i < len; i++){
19137             c = cols[i];
19138             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19139                 // it's the expander..
19140                 expEl  = this.headEls[i];
19141                 continue;
19142             }
19143             totalWidth += c.width;
19144             
19145         }
19146         if (expEl) {
19147             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19148         }
19149         this.headers.setWidth(w-20);
19150
19151         
19152         
19153         
19154     }
19155 });
19156 /*
19157  * Based on:
19158  * Ext JS Library 1.1.1
19159  * Copyright(c) 2006-2007, Ext JS, LLC.
19160  *
19161  * Originally Released Under LGPL - original licence link has changed is not relivant.
19162  *
19163  * Fork - LGPL
19164  * <script type="text/javascript">
19165  */
19166  
19167 /**
19168  * @class Roo.menu.Menu
19169  * @extends Roo.util.Observable
19170  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19171  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19172  * @constructor
19173  * Creates a new Menu
19174  * @param {Object} config Configuration options
19175  */
19176 Roo.menu.Menu = function(config){
19177     Roo.apply(this, config);
19178     this.id = this.id || Roo.id();
19179     this.addEvents({
19180         /**
19181          * @event beforeshow
19182          * Fires before this menu is displayed
19183          * @param {Roo.menu.Menu} this
19184          */
19185         beforeshow : true,
19186         /**
19187          * @event beforehide
19188          * Fires before this menu is hidden
19189          * @param {Roo.menu.Menu} this
19190          */
19191         beforehide : true,
19192         /**
19193          * @event show
19194          * Fires after this menu is displayed
19195          * @param {Roo.menu.Menu} this
19196          */
19197         show : true,
19198         /**
19199          * @event hide
19200          * Fires after this menu is hidden
19201          * @param {Roo.menu.Menu} this
19202          */
19203         hide : true,
19204         /**
19205          * @event click
19206          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19207          * @param {Roo.menu.Menu} this
19208          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19209          * @param {Roo.EventObject} e
19210          */
19211         click : true,
19212         /**
19213          * @event mouseover
19214          * Fires when the mouse is hovering over this menu
19215          * @param {Roo.menu.Menu} this
19216          * @param {Roo.EventObject} e
19217          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19218          */
19219         mouseover : true,
19220         /**
19221          * @event mouseout
19222          * Fires when the mouse exits this menu
19223          * @param {Roo.menu.Menu} this
19224          * @param {Roo.EventObject} e
19225          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19226          */
19227         mouseout : true,
19228         /**
19229          * @event itemclick
19230          * Fires when a menu item contained in this menu is clicked
19231          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19232          * @param {Roo.EventObject} e
19233          */
19234         itemclick: true
19235     });
19236     if (this.registerMenu) {
19237         Roo.menu.MenuMgr.register(this);
19238     }
19239     
19240     var mis = this.items;
19241     this.items = new Roo.util.MixedCollection();
19242     if(mis){
19243         this.add.apply(this, mis);
19244     }
19245 };
19246
19247 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19248     /**
19249      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19250      */
19251     minWidth : 120,
19252     /**
19253      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19254      * for bottom-right shadow (defaults to "sides")
19255      */
19256     shadow : "sides",
19257     /**
19258      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19259      * this menu (defaults to "tl-tr?")
19260      */
19261     subMenuAlign : "tl-tr?",
19262     /**
19263      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19264      * relative to its element of origin (defaults to "tl-bl?")
19265      */
19266     defaultAlign : "tl-bl?",
19267     /**
19268      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19269      */
19270     allowOtherMenus : false,
19271     /**
19272      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19273      */
19274     registerMenu : true,
19275
19276     hidden:true,
19277
19278     // private
19279     render : function(){
19280         if(this.el){
19281             return;
19282         }
19283         var el = this.el = new Roo.Layer({
19284             cls: "x-menu",
19285             shadow:this.shadow,
19286             constrain: false,
19287             parentEl: this.parentEl || document.body,
19288             zindex:15000
19289         });
19290
19291         this.keyNav = new Roo.menu.MenuNav(this);
19292
19293         if(this.plain){
19294             el.addClass("x-menu-plain");
19295         }
19296         if(this.cls){
19297             el.addClass(this.cls);
19298         }
19299         // generic focus element
19300         this.focusEl = el.createChild({
19301             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19302         });
19303         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19304         ul.on("click", this.onClick, this);
19305         ul.on("mouseover", this.onMouseOver, this);
19306         ul.on("mouseout", this.onMouseOut, this);
19307         this.items.each(function(item){
19308             var li = document.createElement("li");
19309             li.className = "x-menu-list-item";
19310             ul.dom.appendChild(li);
19311             item.render(li, this);
19312         }, this);
19313         this.ul = ul;
19314         this.autoWidth();
19315     },
19316
19317     // private
19318     autoWidth : function(){
19319         var el = this.el, ul = this.ul;
19320         if(!el){
19321             return;
19322         }
19323         var w = this.width;
19324         if(w){
19325             el.setWidth(w);
19326         }else if(Roo.isIE){
19327             el.setWidth(this.minWidth);
19328             var t = el.dom.offsetWidth; // force recalc
19329             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19330         }
19331     },
19332
19333     // private
19334     delayAutoWidth : function(){
19335         if(this.rendered){
19336             if(!this.awTask){
19337                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19338             }
19339             this.awTask.delay(20);
19340         }
19341     },
19342
19343     // private
19344     findTargetItem : function(e){
19345         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19346         if(t && t.menuItemId){
19347             return this.items.get(t.menuItemId);
19348         }
19349     },
19350
19351     // private
19352     onClick : function(e){
19353         var t;
19354         if(t = this.findTargetItem(e)){
19355             t.onClick(e);
19356             this.fireEvent("click", this, t, e);
19357         }
19358     },
19359
19360     // private
19361     setActiveItem : function(item, autoExpand){
19362         if(item != this.activeItem){
19363             if(this.activeItem){
19364                 this.activeItem.deactivate();
19365             }
19366             this.activeItem = item;
19367             item.activate(autoExpand);
19368         }else if(autoExpand){
19369             item.expandMenu();
19370         }
19371     },
19372
19373     // private
19374     tryActivate : function(start, step){
19375         var items = this.items;
19376         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19377             var item = items.get(i);
19378             if(!item.disabled && item.canActivate){
19379                 this.setActiveItem(item, false);
19380                 return item;
19381             }
19382         }
19383         return false;
19384     },
19385
19386     // private
19387     onMouseOver : function(e){
19388         var t;
19389         if(t = this.findTargetItem(e)){
19390             if(t.canActivate && !t.disabled){
19391                 this.setActiveItem(t, true);
19392             }
19393         }
19394         this.fireEvent("mouseover", this, e, t);
19395     },
19396
19397     // private
19398     onMouseOut : function(e){
19399         var t;
19400         if(t = this.findTargetItem(e)){
19401             if(t == this.activeItem && t.shouldDeactivate(e)){
19402                 this.activeItem.deactivate();
19403                 delete this.activeItem;
19404             }
19405         }
19406         this.fireEvent("mouseout", this, e, t);
19407     },
19408
19409     /**
19410      * Read-only.  Returns true if the menu is currently displayed, else false.
19411      * @type Boolean
19412      */
19413     isVisible : function(){
19414         return this.el && !this.hidden;
19415     },
19416
19417     /**
19418      * Displays this menu relative to another element
19419      * @param {String/HTMLElement/Roo.Element} element The element to align to
19420      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19421      * the element (defaults to this.defaultAlign)
19422      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19423      */
19424     show : function(el, pos, parentMenu){
19425         this.parentMenu = parentMenu;
19426         if(!this.el){
19427             this.render();
19428         }
19429         this.fireEvent("beforeshow", this);
19430         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19431     },
19432
19433     /**
19434      * Displays this menu at a specific xy position
19435      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19436      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19437      */
19438     showAt : function(xy, parentMenu, /* private: */_e){
19439         this.parentMenu = parentMenu;
19440         if(!this.el){
19441             this.render();
19442         }
19443         if(_e !== false){
19444             this.fireEvent("beforeshow", this);
19445             xy = this.el.adjustForConstraints(xy);
19446         }
19447         this.el.setXY(xy);
19448         this.el.show();
19449         this.hidden = false;
19450         this.focus();
19451         this.fireEvent("show", this);
19452     },
19453
19454     focus : function(){
19455         if(!this.hidden){
19456             this.doFocus.defer(50, this);
19457         }
19458     },
19459
19460     doFocus : function(){
19461         if(!this.hidden){
19462             this.focusEl.focus();
19463         }
19464     },
19465
19466     /**
19467      * Hides this menu and optionally all parent menus
19468      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19469      */
19470     hide : function(deep){
19471         if(this.el && this.isVisible()){
19472             this.fireEvent("beforehide", this);
19473             if(this.activeItem){
19474                 this.activeItem.deactivate();
19475                 this.activeItem = null;
19476             }
19477             this.el.hide();
19478             this.hidden = true;
19479             this.fireEvent("hide", this);
19480         }
19481         if(deep === true && this.parentMenu){
19482             this.parentMenu.hide(true);
19483         }
19484     },
19485
19486     /**
19487      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19488      * Any of the following are valid:
19489      * <ul>
19490      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19491      * <li>An HTMLElement object which will be converted to a menu item</li>
19492      * <li>A menu item config object that will be created as a new menu item</li>
19493      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19494      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19495      * </ul>
19496      * Usage:
19497      * <pre><code>
19498 // Create the menu
19499 var menu = new Roo.menu.Menu();
19500
19501 // Create a menu item to add by reference
19502 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19503
19504 // Add a bunch of items at once using different methods.
19505 // Only the last item added will be returned.
19506 var item = menu.add(
19507     menuItem,                // add existing item by ref
19508     'Dynamic Item',          // new TextItem
19509     '-',                     // new separator
19510     { text: 'Config Item' }  // new item by config
19511 );
19512 </code></pre>
19513      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19514      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19515      */
19516     add : function(){
19517         var a = arguments, l = a.length, item;
19518         for(var i = 0; i < l; i++){
19519             var el = a[i];
19520             if ((typeof(el) == "object") && el.xtype && el.xns) {
19521                 el = Roo.factory(el, Roo.menu);
19522             }
19523             
19524             if(el.render){ // some kind of Item
19525                 item = this.addItem(el);
19526             }else if(typeof el == "string"){ // string
19527                 if(el == "separator" || el == "-"){
19528                     item = this.addSeparator();
19529                 }else{
19530                     item = this.addText(el);
19531                 }
19532             }else if(el.tagName || el.el){ // element
19533                 item = this.addElement(el);
19534             }else if(typeof el == "object"){ // must be menu item config?
19535                 item = this.addMenuItem(el);
19536             }
19537         }
19538         return item;
19539     },
19540
19541     /**
19542      * Returns this menu's underlying {@link Roo.Element} object
19543      * @return {Roo.Element} The element
19544      */
19545     getEl : function(){
19546         if(!this.el){
19547             this.render();
19548         }
19549         return this.el;
19550     },
19551
19552     /**
19553      * Adds a separator bar to the menu
19554      * @return {Roo.menu.Item} The menu item that was added
19555      */
19556     addSeparator : function(){
19557         return this.addItem(new Roo.menu.Separator());
19558     },
19559
19560     /**
19561      * Adds an {@link Roo.Element} object to the menu
19562      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19563      * @return {Roo.menu.Item} The menu item that was added
19564      */
19565     addElement : function(el){
19566         return this.addItem(new Roo.menu.BaseItem(el));
19567     },
19568
19569     /**
19570      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19571      * @param {Roo.menu.Item} item The menu item to add
19572      * @return {Roo.menu.Item} The menu item that was added
19573      */
19574     addItem : function(item){
19575         this.items.add(item);
19576         if(this.ul){
19577             var li = document.createElement("li");
19578             li.className = "x-menu-list-item";
19579             this.ul.dom.appendChild(li);
19580             item.render(li, this);
19581             this.delayAutoWidth();
19582         }
19583         return item;
19584     },
19585
19586     /**
19587      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19588      * @param {Object} config A MenuItem config object
19589      * @return {Roo.menu.Item} The menu item that was added
19590      */
19591     addMenuItem : function(config){
19592         if(!(config instanceof Roo.menu.Item)){
19593             if(typeof config.checked == "boolean"){ // must be check menu item config?
19594                 config = new Roo.menu.CheckItem(config);
19595             }else{
19596                 config = new Roo.menu.Item(config);
19597             }
19598         }
19599         return this.addItem(config);
19600     },
19601
19602     /**
19603      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19604      * @param {String} text The text to display in the menu item
19605      * @return {Roo.menu.Item} The menu item that was added
19606      */
19607     addText : function(text){
19608         return this.addItem(new Roo.menu.TextItem({ text : text }));
19609     },
19610
19611     /**
19612      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19613      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19614      * @param {Roo.menu.Item} item The menu item to add
19615      * @return {Roo.menu.Item} The menu item that was added
19616      */
19617     insert : function(index, item){
19618         this.items.insert(index, item);
19619         if(this.ul){
19620             var li = document.createElement("li");
19621             li.className = "x-menu-list-item";
19622             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19623             item.render(li, this);
19624             this.delayAutoWidth();
19625         }
19626         return item;
19627     },
19628
19629     /**
19630      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19631      * @param {Roo.menu.Item} item The menu item to remove
19632      */
19633     remove : function(item){
19634         this.items.removeKey(item.id);
19635         item.destroy();
19636     },
19637
19638     /**
19639      * Removes and destroys all items in the menu
19640      */
19641     removeAll : function(){
19642         var f;
19643         while(f = this.items.first()){
19644             this.remove(f);
19645         }
19646     }
19647 });
19648
19649 // MenuNav is a private utility class used internally by the Menu
19650 Roo.menu.MenuNav = function(menu){
19651     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19652     this.scope = this.menu = menu;
19653 };
19654
19655 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19656     doRelay : function(e, h){
19657         var k = e.getKey();
19658         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19659             this.menu.tryActivate(0, 1);
19660             return false;
19661         }
19662         return h.call(this.scope || this, e, this.menu);
19663     },
19664
19665     up : function(e, m){
19666         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19667             m.tryActivate(m.items.length-1, -1);
19668         }
19669     },
19670
19671     down : function(e, m){
19672         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19673             m.tryActivate(0, 1);
19674         }
19675     },
19676
19677     right : function(e, m){
19678         if(m.activeItem){
19679             m.activeItem.expandMenu(true);
19680         }
19681     },
19682
19683     left : function(e, m){
19684         m.hide();
19685         if(m.parentMenu && m.parentMenu.activeItem){
19686             m.parentMenu.activeItem.activate();
19687         }
19688     },
19689
19690     enter : function(e, m){
19691         if(m.activeItem){
19692             e.stopPropagation();
19693             m.activeItem.onClick(e);
19694             m.fireEvent("click", this, m.activeItem);
19695             return true;
19696         }
19697     }
19698 });/*
19699  * Based on:
19700  * Ext JS Library 1.1.1
19701  * Copyright(c) 2006-2007, Ext JS, LLC.
19702  *
19703  * Originally Released Under LGPL - original licence link has changed is not relivant.
19704  *
19705  * Fork - LGPL
19706  * <script type="text/javascript">
19707  */
19708  
19709 /**
19710  * @class Roo.menu.MenuMgr
19711  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19712  * @singleton
19713  */
19714 Roo.menu.MenuMgr = function(){
19715    var menus, active, groups = {}, attached = false, lastShow = new Date();
19716
19717    // private - called when first menu is created
19718    function init(){
19719        menus = {};
19720        active = new Roo.util.MixedCollection();
19721        Roo.get(document).addKeyListener(27, function(){
19722            if(active.length > 0){
19723                hideAll();
19724            }
19725        });
19726    }
19727
19728    // private
19729    function hideAll(){
19730        if(active && active.length > 0){
19731            var c = active.clone();
19732            c.each(function(m){
19733                m.hide();
19734            });
19735        }
19736    }
19737
19738    // private
19739    function onHide(m){
19740        active.remove(m);
19741        if(active.length < 1){
19742            Roo.get(document).un("mousedown", onMouseDown);
19743            attached = false;
19744        }
19745    }
19746
19747    // private
19748    function onShow(m){
19749        var last = active.last();
19750        lastShow = new Date();
19751        active.add(m);
19752        if(!attached){
19753            Roo.get(document).on("mousedown", onMouseDown);
19754            attached = true;
19755        }
19756        if(m.parentMenu){
19757           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19758           m.parentMenu.activeChild = m;
19759        }else if(last && last.isVisible()){
19760           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19761        }
19762    }
19763
19764    // private
19765    function onBeforeHide(m){
19766        if(m.activeChild){
19767            m.activeChild.hide();
19768        }
19769        if(m.autoHideTimer){
19770            clearTimeout(m.autoHideTimer);
19771            delete m.autoHideTimer;
19772        }
19773    }
19774
19775    // private
19776    function onBeforeShow(m){
19777        var pm = m.parentMenu;
19778        if(!pm && !m.allowOtherMenus){
19779            hideAll();
19780        }else if(pm && pm.activeChild && active != m){
19781            pm.activeChild.hide();
19782        }
19783    }
19784
19785    // private
19786    function onMouseDown(e){
19787        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19788            hideAll();
19789        }
19790    }
19791
19792    // private
19793    function onBeforeCheck(mi, state){
19794        if(state){
19795            var g = groups[mi.group];
19796            for(var i = 0, l = g.length; i < l; i++){
19797                if(g[i] != mi){
19798                    g[i].setChecked(false);
19799                }
19800            }
19801        }
19802    }
19803
19804    return {
19805
19806        /**
19807         * Hides all menus that are currently visible
19808         */
19809        hideAll : function(){
19810             hideAll();  
19811        },
19812
19813        // private
19814        register : function(menu){
19815            if(!menus){
19816                init();
19817            }
19818            menus[menu.id] = menu;
19819            menu.on("beforehide", onBeforeHide);
19820            menu.on("hide", onHide);
19821            menu.on("beforeshow", onBeforeShow);
19822            menu.on("show", onShow);
19823            var g = menu.group;
19824            if(g && menu.events["checkchange"]){
19825                if(!groups[g]){
19826                    groups[g] = [];
19827                }
19828                groups[g].push(menu);
19829                menu.on("checkchange", onCheck);
19830            }
19831        },
19832
19833         /**
19834          * Returns a {@link Roo.menu.Menu} object
19835          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19836          * be used to generate and return a new Menu instance.
19837          */
19838        get : function(menu){
19839            if(typeof menu == "string"){ // menu id
19840                return menus[menu];
19841            }else if(menu.events){  // menu instance
19842                return menu;
19843            }else if(typeof menu.length == 'number'){ // array of menu items?
19844                return new Roo.menu.Menu({items:menu});
19845            }else{ // otherwise, must be a config
19846                return new Roo.menu.Menu(menu);
19847            }
19848        },
19849
19850        // private
19851        unregister : function(menu){
19852            delete menus[menu.id];
19853            menu.un("beforehide", onBeforeHide);
19854            menu.un("hide", onHide);
19855            menu.un("beforeshow", onBeforeShow);
19856            menu.un("show", onShow);
19857            var g = menu.group;
19858            if(g && menu.events["checkchange"]){
19859                groups[g].remove(menu);
19860                menu.un("checkchange", onCheck);
19861            }
19862        },
19863
19864        // private
19865        registerCheckable : function(menuItem){
19866            var g = menuItem.group;
19867            if(g){
19868                if(!groups[g]){
19869                    groups[g] = [];
19870                }
19871                groups[g].push(menuItem);
19872                menuItem.on("beforecheckchange", onBeforeCheck);
19873            }
19874        },
19875
19876        // private
19877        unregisterCheckable : function(menuItem){
19878            var g = menuItem.group;
19879            if(g){
19880                groups[g].remove(menuItem);
19881                menuItem.un("beforecheckchange", onBeforeCheck);
19882            }
19883        }
19884    };
19885 }();/*
19886  * Based on:
19887  * Ext JS Library 1.1.1
19888  * Copyright(c) 2006-2007, Ext JS, LLC.
19889  *
19890  * Originally Released Under LGPL - original licence link has changed is not relivant.
19891  *
19892  * Fork - LGPL
19893  * <script type="text/javascript">
19894  */
19895  
19896
19897 /**
19898  * @class Roo.menu.BaseItem
19899  * @extends Roo.Component
19900  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19901  * management and base configuration options shared by all menu components.
19902  * @constructor
19903  * Creates a new BaseItem
19904  * @param {Object} config Configuration options
19905  */
19906 Roo.menu.BaseItem = function(config){
19907     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19908
19909     this.addEvents({
19910         /**
19911          * @event click
19912          * Fires when this item is clicked
19913          * @param {Roo.menu.BaseItem} this
19914          * @param {Roo.EventObject} e
19915          */
19916         click: true,
19917         /**
19918          * @event activate
19919          * Fires when this item is activated
19920          * @param {Roo.menu.BaseItem} this
19921          */
19922         activate : true,
19923         /**
19924          * @event deactivate
19925          * Fires when this item is deactivated
19926          * @param {Roo.menu.BaseItem} this
19927          */
19928         deactivate : true
19929     });
19930
19931     if(this.handler){
19932         this.on("click", this.handler, this.scope, true);
19933     }
19934 };
19935
19936 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19937     /**
19938      * @cfg {Function} handler
19939      * A function that will handle the click event of this menu item (defaults to undefined)
19940      */
19941     /**
19942      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19943      */
19944     canActivate : false,
19945     /**
19946      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19947      */
19948     activeClass : "x-menu-item-active",
19949     /**
19950      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19951      */
19952     hideOnClick : true,
19953     /**
19954      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19955      */
19956     hideDelay : 100,
19957
19958     // private
19959     ctype: "Roo.menu.BaseItem",
19960
19961     // private
19962     actionMode : "container",
19963
19964     // private
19965     render : function(container, parentMenu){
19966         this.parentMenu = parentMenu;
19967         Roo.menu.BaseItem.superclass.render.call(this, container);
19968         this.container.menuItemId = this.id;
19969     },
19970
19971     // private
19972     onRender : function(container, position){
19973         this.el = Roo.get(this.el);
19974         container.dom.appendChild(this.el.dom);
19975     },
19976
19977     // private
19978     onClick : function(e){
19979         if(!this.disabled && this.fireEvent("click", this, e) !== false
19980                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19981             this.handleClick(e);
19982         }else{
19983             e.stopEvent();
19984         }
19985     },
19986
19987     // private
19988     activate : function(){
19989         if(this.disabled){
19990             return false;
19991         }
19992         var li = this.container;
19993         li.addClass(this.activeClass);
19994         this.region = li.getRegion().adjust(2, 2, -2, -2);
19995         this.fireEvent("activate", this);
19996         return true;
19997     },
19998
19999     // private
20000     deactivate : function(){
20001         this.container.removeClass(this.activeClass);
20002         this.fireEvent("deactivate", this);
20003     },
20004
20005     // private
20006     shouldDeactivate : function(e){
20007         return !this.region || !this.region.contains(e.getPoint());
20008     },
20009
20010     // private
20011     handleClick : function(e){
20012         if(this.hideOnClick){
20013             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20014         }
20015     },
20016
20017     // private
20018     expandMenu : function(autoActivate){
20019         // do nothing
20020     },
20021
20022     // private
20023     hideMenu : function(){
20024         // do nothing
20025     }
20026 });/*
20027  * Based on:
20028  * Ext JS Library 1.1.1
20029  * Copyright(c) 2006-2007, Ext JS, LLC.
20030  *
20031  * Originally Released Under LGPL - original licence link has changed is not relivant.
20032  *
20033  * Fork - LGPL
20034  * <script type="text/javascript">
20035  */
20036  
20037 /**
20038  * @class Roo.menu.Adapter
20039  * @extends Roo.menu.BaseItem
20040  * 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.
20041  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20042  * @constructor
20043  * Creates a new Adapter
20044  * @param {Object} config Configuration options
20045  */
20046 Roo.menu.Adapter = function(component, config){
20047     Roo.menu.Adapter.superclass.constructor.call(this, config);
20048     this.component = component;
20049 };
20050 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20051     // private
20052     canActivate : true,
20053
20054     // private
20055     onRender : function(container, position){
20056         this.component.render(container);
20057         this.el = this.component.getEl();
20058     },
20059
20060     // private
20061     activate : function(){
20062         if(this.disabled){
20063             return false;
20064         }
20065         this.component.focus();
20066         this.fireEvent("activate", this);
20067         return true;
20068     },
20069
20070     // private
20071     deactivate : function(){
20072         this.fireEvent("deactivate", this);
20073     },
20074
20075     // private
20076     disable : function(){
20077         this.component.disable();
20078         Roo.menu.Adapter.superclass.disable.call(this);
20079     },
20080
20081     // private
20082     enable : function(){
20083         this.component.enable();
20084         Roo.menu.Adapter.superclass.enable.call(this);
20085     }
20086 });/*
20087  * Based on:
20088  * Ext JS Library 1.1.1
20089  * Copyright(c) 2006-2007, Ext JS, LLC.
20090  *
20091  * Originally Released Under LGPL - original licence link has changed is not relivant.
20092  *
20093  * Fork - LGPL
20094  * <script type="text/javascript">
20095  */
20096
20097 /**
20098  * @class Roo.menu.TextItem
20099  * @extends Roo.menu.BaseItem
20100  * Adds a static text string to a menu, usually used as either a heading or group separator.
20101  * Note: old style constructor with text is still supported.
20102  * 
20103  * @constructor
20104  * Creates a new TextItem
20105  * @param {Object} cfg Configuration
20106  */
20107 Roo.menu.TextItem = function(cfg){
20108     if (typeof(cfg) == 'string') {
20109         this.text = cfg;
20110     } else {
20111         Roo.apply(this,cfg);
20112     }
20113     
20114     Roo.menu.TextItem.superclass.constructor.call(this);
20115 };
20116
20117 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20118     /**
20119      * @cfg {Boolean} text Text to show on item.
20120      */
20121     text : '',
20122     
20123     /**
20124      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20125      */
20126     hideOnClick : false,
20127     /**
20128      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20129      */
20130     itemCls : "x-menu-text",
20131
20132     // private
20133     onRender : function(){
20134         var s = document.createElement("span");
20135         s.className = this.itemCls;
20136         s.innerHTML = this.text;
20137         this.el = s;
20138         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20139     }
20140 });/*
20141  * Based on:
20142  * Ext JS Library 1.1.1
20143  * Copyright(c) 2006-2007, Ext JS, LLC.
20144  *
20145  * Originally Released Under LGPL - original licence link has changed is not relivant.
20146  *
20147  * Fork - LGPL
20148  * <script type="text/javascript">
20149  */
20150
20151 /**
20152  * @class Roo.menu.Separator
20153  * @extends Roo.menu.BaseItem
20154  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20155  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20156  * @constructor
20157  * @param {Object} config Configuration options
20158  */
20159 Roo.menu.Separator = function(config){
20160     Roo.menu.Separator.superclass.constructor.call(this, config);
20161 };
20162
20163 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20164     /**
20165      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20166      */
20167     itemCls : "x-menu-sep",
20168     /**
20169      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20170      */
20171     hideOnClick : false,
20172
20173     // private
20174     onRender : function(li){
20175         var s = document.createElement("span");
20176         s.className = this.itemCls;
20177         s.innerHTML = "&#160;";
20178         this.el = s;
20179         li.addClass("x-menu-sep-li");
20180         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20181     }
20182 });/*
20183  * Based on:
20184  * Ext JS Library 1.1.1
20185  * Copyright(c) 2006-2007, Ext JS, LLC.
20186  *
20187  * Originally Released Under LGPL - original licence link has changed is not relivant.
20188  *
20189  * Fork - LGPL
20190  * <script type="text/javascript">
20191  */
20192 /**
20193  * @class Roo.menu.Item
20194  * @extends Roo.menu.BaseItem
20195  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20196  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20197  * activation and click handling.
20198  * @constructor
20199  * Creates a new Item
20200  * @param {Object} config Configuration options
20201  */
20202 Roo.menu.Item = function(config){
20203     Roo.menu.Item.superclass.constructor.call(this, config);
20204     if(this.menu){
20205         this.menu = Roo.menu.MenuMgr.get(this.menu);
20206     }
20207 };
20208 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20209     /**
20210      * @cfg {String} icon
20211      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20212      */
20213     /**
20214      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20215      */
20216     itemCls : "x-menu-item",
20217     /**
20218      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20219      */
20220     canActivate : true,
20221     /**
20222      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20223      */
20224     showDelay: 200,
20225     // doc'd in BaseItem
20226     hideDelay: 200,
20227
20228     // private
20229     ctype: "Roo.menu.Item",
20230     
20231     // private
20232     onRender : function(container, position){
20233         var el = document.createElement("a");
20234         el.hideFocus = true;
20235         el.unselectable = "on";
20236         el.href = this.href || "#";
20237         if(this.hrefTarget){
20238             el.target = this.hrefTarget;
20239         }
20240         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20241         el.innerHTML = String.format(
20242                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
20243                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
20244         this.el = el;
20245         Roo.menu.Item.superclass.onRender.call(this, container, position);
20246     },
20247
20248     /**
20249      * Sets the text to display in this menu item
20250      * @param {String} text The text to display
20251      */
20252     setText : function(text){
20253         this.text = text;
20254         if(this.rendered){
20255             this.el.update(String.format(
20256                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
20257                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20258             this.parentMenu.autoWidth();
20259         }
20260     },
20261
20262     // private
20263     handleClick : function(e){
20264         if(!this.href){ // if no link defined, stop the event automatically
20265             e.stopEvent();
20266         }
20267         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20268     },
20269
20270     // private
20271     activate : function(autoExpand){
20272         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20273             this.focus();
20274             if(autoExpand){
20275                 this.expandMenu();
20276             }
20277         }
20278         return true;
20279     },
20280
20281     // private
20282     shouldDeactivate : function(e){
20283         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20284             if(this.menu && this.menu.isVisible()){
20285                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20286             }
20287             return true;
20288         }
20289         return false;
20290     },
20291
20292     // private
20293     deactivate : function(){
20294         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20295         this.hideMenu();
20296     },
20297
20298     // private
20299     expandMenu : function(autoActivate){
20300         if(!this.disabled && this.menu){
20301             clearTimeout(this.hideTimer);
20302             delete this.hideTimer;
20303             if(!this.menu.isVisible() && !this.showTimer){
20304                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20305             }else if (this.menu.isVisible() && autoActivate){
20306                 this.menu.tryActivate(0, 1);
20307             }
20308         }
20309     },
20310
20311     // private
20312     deferExpand : function(autoActivate){
20313         delete this.showTimer;
20314         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20315         if(autoActivate){
20316             this.menu.tryActivate(0, 1);
20317         }
20318     },
20319
20320     // private
20321     hideMenu : function(){
20322         clearTimeout(this.showTimer);
20323         delete this.showTimer;
20324         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20325             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20326         }
20327     },
20328
20329     // private
20330     deferHide : function(){
20331         delete this.hideTimer;
20332         this.menu.hide();
20333     }
20334 });/*
20335  * Based on:
20336  * Ext JS Library 1.1.1
20337  * Copyright(c) 2006-2007, Ext JS, LLC.
20338  *
20339  * Originally Released Under LGPL - original licence link has changed is not relivant.
20340  *
20341  * Fork - LGPL
20342  * <script type="text/javascript">
20343  */
20344  
20345 /**
20346  * @class Roo.menu.CheckItem
20347  * @extends Roo.menu.Item
20348  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20349  * @constructor
20350  * Creates a new CheckItem
20351  * @param {Object} config Configuration options
20352  */
20353 Roo.menu.CheckItem = function(config){
20354     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20355     this.addEvents({
20356         /**
20357          * @event beforecheckchange
20358          * Fires before the checked value is set, providing an opportunity to cancel if needed
20359          * @param {Roo.menu.CheckItem} this
20360          * @param {Boolean} checked The new checked value that will be set
20361          */
20362         "beforecheckchange" : true,
20363         /**
20364          * @event checkchange
20365          * Fires after the checked value has been set
20366          * @param {Roo.menu.CheckItem} this
20367          * @param {Boolean} checked The checked value that was set
20368          */
20369         "checkchange" : true
20370     });
20371     if(this.checkHandler){
20372         this.on('checkchange', this.checkHandler, this.scope);
20373     }
20374 };
20375 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20376     /**
20377      * @cfg {String} group
20378      * All check items with the same group name will automatically be grouped into a single-select
20379      * radio button group (defaults to '')
20380      */
20381     /**
20382      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20383      */
20384     itemCls : "x-menu-item x-menu-check-item",
20385     /**
20386      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20387      */
20388     groupClass : "x-menu-group-item",
20389
20390     /**
20391      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20392      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20393      * initialized with checked = true will be rendered as checked.
20394      */
20395     checked: false,
20396
20397     // private
20398     ctype: "Roo.menu.CheckItem",
20399
20400     // private
20401     onRender : function(c){
20402         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20403         if(this.group){
20404             this.el.addClass(this.groupClass);
20405         }
20406         Roo.menu.MenuMgr.registerCheckable(this);
20407         if(this.checked){
20408             this.checked = false;
20409             this.setChecked(true, true);
20410         }
20411     },
20412
20413     // private
20414     destroy : function(){
20415         if(this.rendered){
20416             Roo.menu.MenuMgr.unregisterCheckable(this);
20417         }
20418         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20419     },
20420
20421     /**
20422      * Set the checked state of this item
20423      * @param {Boolean} checked The new checked value
20424      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20425      */
20426     setChecked : function(state, suppressEvent){
20427         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20428             if(this.container){
20429                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20430             }
20431             this.checked = state;
20432             if(suppressEvent !== true){
20433                 this.fireEvent("checkchange", this, state);
20434             }
20435         }
20436     },
20437
20438     // private
20439     handleClick : function(e){
20440        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20441            this.setChecked(!this.checked);
20442        }
20443        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20444     }
20445 });/*
20446  * Based on:
20447  * Ext JS Library 1.1.1
20448  * Copyright(c) 2006-2007, Ext JS, LLC.
20449  *
20450  * Originally Released Under LGPL - original licence link has changed is not relivant.
20451  *
20452  * Fork - LGPL
20453  * <script type="text/javascript">
20454  */
20455  
20456 /**
20457  * @class Roo.menu.DateItem
20458  * @extends Roo.menu.Adapter
20459  * A menu item that wraps the {@link Roo.DatPicker} component.
20460  * @constructor
20461  * Creates a new DateItem
20462  * @param {Object} config Configuration options
20463  */
20464 Roo.menu.DateItem = function(config){
20465     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20466     /** The Roo.DatePicker object @type Roo.DatePicker */
20467     this.picker = this.component;
20468     this.addEvents({select: true});
20469     
20470     this.picker.on("render", function(picker){
20471         picker.getEl().swallowEvent("click");
20472         picker.container.addClass("x-menu-date-item");
20473     });
20474
20475     this.picker.on("select", this.onSelect, this);
20476 };
20477
20478 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20479     // private
20480     onSelect : function(picker, date){
20481         this.fireEvent("select", this, date, picker);
20482         Roo.menu.DateItem.superclass.handleClick.call(this);
20483     }
20484 });/*
20485  * Based on:
20486  * Ext JS Library 1.1.1
20487  * Copyright(c) 2006-2007, Ext JS, LLC.
20488  *
20489  * Originally Released Under LGPL - original licence link has changed is not relivant.
20490  *
20491  * Fork - LGPL
20492  * <script type="text/javascript">
20493  */
20494  
20495 /**
20496  * @class Roo.menu.ColorItem
20497  * @extends Roo.menu.Adapter
20498  * A menu item that wraps the {@link Roo.ColorPalette} component.
20499  * @constructor
20500  * Creates a new ColorItem
20501  * @param {Object} config Configuration options
20502  */
20503 Roo.menu.ColorItem = function(config){
20504     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20505     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20506     this.palette = this.component;
20507     this.relayEvents(this.palette, ["select"]);
20508     if(this.selectHandler){
20509         this.on('select', this.selectHandler, this.scope);
20510     }
20511 };
20512 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20513  * Based on:
20514  * Ext JS Library 1.1.1
20515  * Copyright(c) 2006-2007, Ext JS, LLC.
20516  *
20517  * Originally Released Under LGPL - original licence link has changed is not relivant.
20518  *
20519  * Fork - LGPL
20520  * <script type="text/javascript">
20521  */
20522  
20523
20524 /**
20525  * @class Roo.menu.DateMenu
20526  * @extends Roo.menu.Menu
20527  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20528  * @constructor
20529  * Creates a new DateMenu
20530  * @param {Object} config Configuration options
20531  */
20532 Roo.menu.DateMenu = function(config){
20533     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20534     this.plain = true;
20535     var di = new Roo.menu.DateItem(config);
20536     this.add(di);
20537     /**
20538      * The {@link Roo.DatePicker} instance for this DateMenu
20539      * @type DatePicker
20540      */
20541     this.picker = di.picker;
20542     /**
20543      * @event select
20544      * @param {DatePicker} picker
20545      * @param {Date} date
20546      */
20547     this.relayEvents(di, ["select"]);
20548
20549     this.on('beforeshow', function(){
20550         if(this.picker){
20551             this.picker.hideMonthPicker(true);
20552         }
20553     }, this);
20554 };
20555 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20556     cls:'x-date-menu'
20557 });/*
20558  * Based on:
20559  * Ext JS Library 1.1.1
20560  * Copyright(c) 2006-2007, Ext JS, LLC.
20561  *
20562  * Originally Released Under LGPL - original licence link has changed is not relivant.
20563  *
20564  * Fork - LGPL
20565  * <script type="text/javascript">
20566  */
20567  
20568
20569 /**
20570  * @class Roo.menu.ColorMenu
20571  * @extends Roo.menu.Menu
20572  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20573  * @constructor
20574  * Creates a new ColorMenu
20575  * @param {Object} config Configuration options
20576  */
20577 Roo.menu.ColorMenu = function(config){
20578     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20579     this.plain = true;
20580     var ci = new Roo.menu.ColorItem(config);
20581     this.add(ci);
20582     /**
20583      * The {@link Roo.ColorPalette} instance for this ColorMenu
20584      * @type ColorPalette
20585      */
20586     this.palette = ci.palette;
20587     /**
20588      * @event select
20589      * @param {ColorPalette} palette
20590      * @param {String} color
20591      */
20592     this.relayEvents(ci, ["select"]);
20593 };
20594 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20595  * Based on:
20596  * Ext JS Library 1.1.1
20597  * Copyright(c) 2006-2007, Ext JS, LLC.
20598  *
20599  * Originally Released Under LGPL - original licence link has changed is not relivant.
20600  *
20601  * Fork - LGPL
20602  * <script type="text/javascript">
20603  */
20604  
20605 /**
20606  * @class Roo.form.Field
20607  * @extends Roo.BoxComponent
20608  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20609  * @constructor
20610  * Creates a new Field
20611  * @param {Object} config Configuration options
20612  */
20613 Roo.form.Field = function(config){
20614     Roo.form.Field.superclass.constructor.call(this, config);
20615 };
20616
20617 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20618     /**
20619      * @cfg {String} fieldLabel Label to use when rendering a form.
20620      */
20621        /**
20622      * @cfg {String} qtip Mouse over tip
20623      */
20624      
20625     /**
20626      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20627      */
20628     invalidClass : "x-form-invalid",
20629     /**
20630      * @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")
20631      */
20632     invalidText : "The value in this field is invalid",
20633     /**
20634      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20635      */
20636     focusClass : "x-form-focus",
20637     /**
20638      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20639       automatic validation (defaults to "keyup").
20640      */
20641     validationEvent : "keyup",
20642     /**
20643      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20644      */
20645     validateOnBlur : true,
20646     /**
20647      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20648      */
20649     validationDelay : 250,
20650     /**
20651      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20652      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20653      */
20654     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20655     /**
20656      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20657      */
20658     fieldClass : "x-form-field",
20659     /**
20660      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20661      *<pre>
20662 Value         Description
20663 -----------   ----------------------------------------------------------------------
20664 qtip          Display a quick tip when the user hovers over the field
20665 title         Display a default browser title attribute popup
20666 under         Add a block div beneath the field containing the error text
20667 side          Add an error icon to the right of the field with a popup on hover
20668 [element id]  Add the error text directly to the innerHTML of the specified element
20669 </pre>
20670      */
20671     msgTarget : 'qtip',
20672     /**
20673      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20674      */
20675     msgFx : 'normal',
20676
20677     /**
20678      * @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.
20679      */
20680     readOnly : false,
20681
20682     /**
20683      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20684      */
20685     disabled : false,
20686
20687     /**
20688      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20689      */
20690     inputType : undefined,
20691     
20692     /**
20693      * @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).
20694          */
20695         tabIndex : undefined,
20696         
20697     // private
20698     isFormField : true,
20699
20700     // private
20701     hasFocus : false,
20702     /**
20703      * @property {Roo.Element} fieldEl
20704      * Element Containing the rendered Field (with label etc.)
20705      */
20706     /**
20707      * @cfg {Mixed} value A value to initialize this field with.
20708      */
20709     value : undefined,
20710
20711     /**
20712      * @cfg {String} name The field's HTML name attribute.
20713      */
20714     /**
20715      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20716      */
20717
20718         // private ??
20719         initComponent : function(){
20720         Roo.form.Field.superclass.initComponent.call(this);
20721         this.addEvents({
20722             /**
20723              * @event focus
20724              * Fires when this field receives input focus.
20725              * @param {Roo.form.Field} this
20726              */
20727             focus : true,
20728             /**
20729              * @event blur
20730              * Fires when this field loses input focus.
20731              * @param {Roo.form.Field} this
20732              */
20733             blur : true,
20734             /**
20735              * @event specialkey
20736              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20737              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20738              * @param {Roo.form.Field} this
20739              * @param {Roo.EventObject} e The event object
20740              */
20741             specialkey : true,
20742             /**
20743              * @event change
20744              * Fires just before the field blurs if the field value has changed.
20745              * @param {Roo.form.Field} this
20746              * @param {Mixed} newValue The new value
20747              * @param {Mixed} oldValue The original value
20748              */
20749             change : true,
20750             /**
20751              * @event invalid
20752              * Fires after the field has been marked as invalid.
20753              * @param {Roo.form.Field} this
20754              * @param {String} msg The validation message
20755              */
20756             invalid : true,
20757             /**
20758              * @event valid
20759              * Fires after the field has been validated with no errors.
20760              * @param {Roo.form.Field} this
20761              */
20762             valid : true
20763         });
20764     },
20765
20766     /**
20767      * Returns the name attribute of the field if available
20768      * @return {String} name The field name
20769      */
20770     getName: function(){
20771          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20772     },
20773
20774     // private
20775     onRender : function(ct, position){
20776         Roo.form.Field.superclass.onRender.call(this, ct, position);
20777         if(!this.el){
20778             var cfg = this.getAutoCreate();
20779             if(!cfg.name){
20780                 cfg.name = this.name || this.id;
20781             }
20782             if(this.inputType){
20783                 cfg.type = this.inputType;
20784             }
20785             this.el = ct.createChild(cfg, position);
20786         }
20787         var type = this.el.dom.type;
20788         if(type){
20789             if(type == 'password'){
20790                 type = 'text';
20791             }
20792             this.el.addClass('x-form-'+type);
20793         }
20794         if(this.readOnly){
20795             this.el.dom.readOnly = true;
20796         }
20797         if(this.tabIndex !== undefined){
20798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20799         }
20800
20801         this.el.addClass([this.fieldClass, this.cls]);
20802         this.initValue();
20803     },
20804
20805     /**
20806      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20807      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20808      * @return {Roo.form.Field} this
20809      */
20810     applyTo : function(target){
20811         this.allowDomMove = false;
20812         this.el = Roo.get(target);
20813         this.render(this.el.dom.parentNode);
20814         return this;
20815     },
20816
20817     // private
20818     initValue : function(){
20819         if(this.value !== undefined){
20820             this.setValue(this.value);
20821         }else if(this.el.dom.value.length > 0){
20822             this.setValue(this.el.dom.value);
20823         }
20824     },
20825
20826     /**
20827      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20828      */
20829     isDirty : function() {
20830         if(this.disabled) {
20831             return false;
20832         }
20833         return String(this.getValue()) !== String(this.originalValue);
20834     },
20835
20836     // private
20837     afterRender : function(){
20838         Roo.form.Field.superclass.afterRender.call(this);
20839         this.initEvents();
20840     },
20841
20842     // private
20843     fireKey : function(e){
20844         if(e.isNavKeyPress()){
20845             this.fireEvent("specialkey", this, e);
20846         }
20847     },
20848
20849     /**
20850      * Resets the current field value to the originally loaded value and clears any validation messages
20851      */
20852     reset : function(){
20853         this.setValue(this.originalValue);
20854         this.clearInvalid();
20855     },
20856
20857     // private
20858     initEvents : function(){
20859         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
20860         this.el.on("focus", this.onFocus,  this);
20861         this.el.on("blur", this.onBlur,  this);
20862
20863         // reference to original value for reset
20864         this.originalValue = this.getValue();
20865     },
20866
20867     // private
20868     onFocus : function(){
20869         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20870             this.el.addClass(this.focusClass);
20871         }
20872         if(!this.hasFocus){
20873             this.hasFocus = true;
20874             this.startValue = this.getValue();
20875             this.fireEvent("focus", this);
20876         }
20877     },
20878
20879     beforeBlur : Roo.emptyFn,
20880
20881     // private
20882     onBlur : function(){
20883         this.beforeBlur();
20884         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20885             this.el.removeClass(this.focusClass);
20886         }
20887         this.hasFocus = false;
20888         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20889             this.validate();
20890         }
20891         var v = this.getValue();
20892         if(String(v) !== String(this.startValue)){
20893             this.fireEvent('change', this, v, this.startValue);
20894         }
20895         this.fireEvent("blur", this);
20896     },
20897
20898     /**
20899      * Returns whether or not the field value is currently valid
20900      * @param {Boolean} preventMark True to disable marking the field invalid
20901      * @return {Boolean} True if the value is valid, else false
20902      */
20903     isValid : function(preventMark){
20904         if(this.disabled){
20905             return true;
20906         }
20907         var restore = this.preventMark;
20908         this.preventMark = preventMark === true;
20909         var v = this.validateValue(this.processValue(this.getRawValue()));
20910         this.preventMark = restore;
20911         return v;
20912     },
20913
20914     /**
20915      * Validates the field value
20916      * @return {Boolean} True if the value is valid, else false
20917      */
20918     validate : function(){
20919         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20920             this.clearInvalid();
20921             return true;
20922         }
20923         return false;
20924     },
20925
20926     processValue : function(value){
20927         return value;
20928     },
20929
20930     // private
20931     // Subclasses should provide the validation implementation by overriding this
20932     validateValue : function(value){
20933         return true;
20934     },
20935
20936     /**
20937      * Mark this field as invalid
20938      * @param {String} msg The validation message
20939      */
20940     markInvalid : function(msg){
20941         if(!this.rendered || this.preventMark){ // not rendered
20942             return;
20943         }
20944         this.el.addClass(this.invalidClass);
20945         msg = msg || this.invalidText;
20946         switch(this.msgTarget){
20947             case 'qtip':
20948                 this.el.dom.qtip = msg;
20949                 this.el.dom.qclass = 'x-form-invalid-tip';
20950                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20951                     Roo.QuickTips.enable();
20952                 }
20953                 break;
20954             case 'title':
20955                 this.el.dom.title = msg;
20956                 break;
20957             case 'under':
20958                 if(!this.errorEl){
20959                     var elp = this.el.findParent('.x-form-element', 5, true);
20960                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20961                     this.errorEl.setWidth(elp.getWidth(true)-20);
20962                 }
20963                 this.errorEl.update(msg);
20964                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20965                 break;
20966             case 'side':
20967                 if(!this.errorIcon){
20968                     var elp = this.el.findParent('.x-form-element', 5, true);
20969                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20970                 }
20971                 this.alignErrorIcon();
20972                 this.errorIcon.dom.qtip = msg;
20973                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20974                 this.errorIcon.show();
20975                 this.on('resize', this.alignErrorIcon, this);
20976                 break;
20977             default:
20978                 var t = Roo.getDom(this.msgTarget);
20979                 t.innerHTML = msg;
20980                 t.style.display = this.msgDisplay;
20981                 break;
20982         }
20983         this.fireEvent('invalid', this, msg);
20984     },
20985
20986     // private
20987     alignErrorIcon : function(){
20988         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20989     },
20990
20991     /**
20992      * Clear any invalid styles/messages for this field
20993      */
20994     clearInvalid : function(){
20995         if(!this.rendered || this.preventMark){ // not rendered
20996             return;
20997         }
20998         this.el.removeClass(this.invalidClass);
20999         switch(this.msgTarget){
21000             case 'qtip':
21001                 this.el.dom.qtip = '';
21002                 break;
21003             case 'title':
21004                 this.el.dom.title = '';
21005                 break;
21006             case 'under':
21007                 if(this.errorEl){
21008                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21009                 }
21010                 break;
21011             case 'side':
21012                 if(this.errorIcon){
21013                     this.errorIcon.dom.qtip = '';
21014                     this.errorIcon.hide();
21015                     this.un('resize', this.alignErrorIcon, this);
21016                 }
21017                 break;
21018             default:
21019                 var t = Roo.getDom(this.msgTarget);
21020                 t.innerHTML = '';
21021                 t.style.display = 'none';
21022                 break;
21023         }
21024         this.fireEvent('valid', this);
21025     },
21026
21027     /**
21028      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21029      * @return {Mixed} value The field value
21030      */
21031     getRawValue : function(){
21032         var v = this.el.getValue();
21033         if(v === this.emptyText){
21034             v = '';
21035         }
21036         return v;
21037     },
21038
21039     /**
21040      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21041      * @return {Mixed} value The field value
21042      */
21043     getValue : function(){
21044         var v = this.el.getValue();
21045         if(v === this.emptyText || v === undefined){
21046             v = '';
21047         }
21048         return v;
21049     },
21050
21051     /**
21052      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21053      * @param {Mixed} value The value to set
21054      */
21055     setRawValue : function(v){
21056         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21057     },
21058
21059     /**
21060      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21061      * @param {Mixed} value The value to set
21062      */
21063     setValue : function(v){
21064         this.value = v;
21065         if(this.rendered){
21066             this.el.dom.value = (v === null || v === undefined ? '' : v);
21067             this.validate();
21068         }
21069     },
21070
21071     adjustSize : function(w, h){
21072         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21073         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21074         return s;
21075     },
21076
21077     adjustWidth : function(tag, w){
21078         tag = tag.toLowerCase();
21079         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21080             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21081                 if(tag == 'input'){
21082                     return w + 2;
21083                 }
21084                 if(tag = 'textarea'){
21085                     return w-2;
21086                 }
21087             }else if(Roo.isOpera){
21088                 if(tag == 'input'){
21089                     return w + 2;
21090                 }
21091                 if(tag = 'textarea'){
21092                     return w-2;
21093                 }
21094             }
21095         }
21096         return w;
21097     }
21098 });
21099
21100
21101 // anything other than normal should be considered experimental
21102 Roo.form.Field.msgFx = {
21103     normal : {
21104         show: function(msgEl, f){
21105             msgEl.setDisplayed('block');
21106         },
21107
21108         hide : function(msgEl, f){
21109             msgEl.setDisplayed(false).update('');
21110         }
21111     },
21112
21113     slide : {
21114         show: function(msgEl, f){
21115             msgEl.slideIn('t', {stopFx:true});
21116         },
21117
21118         hide : function(msgEl, f){
21119             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21120         }
21121     },
21122
21123     slideRight : {
21124         show: function(msgEl, f){
21125             msgEl.fixDisplay();
21126             msgEl.alignTo(f.el, 'tl-tr');
21127             msgEl.slideIn('l', {stopFx:true});
21128         },
21129
21130         hide : function(msgEl, f){
21131             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21132         }
21133     }
21134 };/*
21135  * Based on:
21136  * Ext JS Library 1.1.1
21137  * Copyright(c) 2006-2007, Ext JS, LLC.
21138  *
21139  * Originally Released Under LGPL - original licence link has changed is not relivant.
21140  *
21141  * Fork - LGPL
21142  * <script type="text/javascript">
21143  */
21144  
21145
21146 /**
21147  * @class Roo.form.TextField
21148  * @extends Roo.form.Field
21149  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21150  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21151  * @constructor
21152  * Creates a new TextField
21153  * @param {Object} config Configuration options
21154  */
21155 Roo.form.TextField = function(config){
21156     Roo.form.TextField.superclass.constructor.call(this, config);
21157     this.addEvents({
21158         /**
21159          * @event autosize
21160          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21161          * according to the default logic, but this event provides a hook for the developer to apply additional
21162          * logic at runtime to resize the field if needed.
21163              * @param {Roo.form.Field} this This text field
21164              * @param {Number} width The new field width
21165              */
21166         autosize : true
21167     });
21168 };
21169
21170 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21171     /**
21172      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21173      */
21174     grow : false,
21175     /**
21176      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21177      */
21178     growMin : 30,
21179     /**
21180      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21181      */
21182     growMax : 800,
21183     /**
21184      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21185      */
21186     vtype : null,
21187     /**
21188      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21189      */
21190     maskRe : null,
21191     /**
21192      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21193      */
21194     disableKeyFilter : false,
21195     /**
21196      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21197      */
21198     allowBlank : true,
21199     /**
21200      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21201      */
21202     minLength : 0,
21203     /**
21204      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21205      */
21206     maxLength : Number.MAX_VALUE,
21207     /**
21208      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21209      */
21210     minLengthText : "The minimum length for this field is {0}",
21211     /**
21212      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21213      */
21214     maxLengthText : "The maximum length for this field is {0}",
21215     /**
21216      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21217      */
21218     selectOnFocus : false,
21219     /**
21220      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21221      */
21222     blankText : "This field is required",
21223     /**
21224      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21225      * If available, this function will be called only after the basic validators all return true, and will be passed the
21226      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21227      */
21228     validator : null,
21229     /**
21230      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21231      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21232      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21233      */
21234     regex : null,
21235     /**
21236      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21237      */
21238     regexText : "",
21239     /**
21240      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21241      */
21242     emptyText : null,
21243     /**
21244      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21245      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21246      */
21247     emptyClass : 'x-form-empty-field',
21248
21249     // private
21250     initEvents : function(){
21251         Roo.form.TextField.superclass.initEvents.call(this);
21252         if(this.validationEvent == 'keyup'){
21253             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21254             this.el.on('keyup', this.filterValidation, this);
21255         }
21256         else if(this.validationEvent !== false){
21257             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21258         }
21259         if(this.selectOnFocus || this.emptyText){
21260             this.on("focus", this.preFocus, this);
21261             if(this.emptyText){
21262                 this.on('blur', this.postBlur, this);
21263                 this.applyEmptyText();
21264             }
21265         }
21266         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21267             this.el.on("keypress", this.filterKeys, this);
21268         }
21269         if(this.grow){
21270             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21271             this.el.on("click", this.autoSize,  this);
21272         }
21273     },
21274
21275     processValue : function(value){
21276         if(this.stripCharsRe){
21277             var newValue = value.replace(this.stripCharsRe, '');
21278             if(newValue !== value){
21279                 this.setRawValue(newValue);
21280                 return newValue;
21281             }
21282         }
21283         return value;
21284     },
21285
21286     filterValidation : function(e){
21287         if(!e.isNavKeyPress()){
21288             this.validationTask.delay(this.validationDelay);
21289         }
21290     },
21291
21292     // private
21293     onKeyUp : function(e){
21294         if(!e.isNavKeyPress()){
21295             this.autoSize();
21296         }
21297     },
21298
21299     /**
21300      * Resets the current field value to the originally-loaded value and clears any validation messages.
21301      * Also adds emptyText and emptyClass if the original value was blank.
21302      */
21303     reset : function(){
21304         Roo.form.TextField.superclass.reset.call(this);
21305         this.applyEmptyText();
21306     },
21307
21308     applyEmptyText : function(){
21309         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21310             this.setRawValue(this.emptyText);
21311             this.el.addClass(this.emptyClass);
21312         }
21313     },
21314
21315     // private
21316     preFocus : function(){
21317         if(this.emptyText){
21318             if(this.el.dom.value == this.emptyText){
21319                 this.setRawValue('');
21320             }
21321             this.el.removeClass(this.emptyClass);
21322         }
21323         if(this.selectOnFocus){
21324             this.el.dom.select();
21325         }
21326     },
21327
21328     // private
21329     postBlur : function(){
21330         this.applyEmptyText();
21331     },
21332
21333     // private
21334     filterKeys : function(e){
21335         var k = e.getKey();
21336         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21337             return;
21338         }
21339         var c = e.getCharCode(), cc = String.fromCharCode(c);
21340         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21341             return;
21342         }
21343         if(!this.maskRe.test(cc)){
21344             e.stopEvent();
21345         }
21346     },
21347
21348     setValue : function(v){
21349         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21350             this.el.removeClass(this.emptyClass);
21351         }
21352         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21353         this.applyEmptyText();
21354         this.autoSize();
21355     },
21356
21357     /**
21358      * Validates a value according to the field's validation rules and marks the field as invalid
21359      * if the validation fails
21360      * @param {Mixed} value The value to validate
21361      * @return {Boolean} True if the value is valid, else false
21362      */
21363     validateValue : function(value){
21364         if(value.length < 1 || value === this.emptyText){ // if it's blank
21365              if(this.allowBlank){
21366                 this.clearInvalid();
21367                 return true;
21368              }else{
21369                 this.markInvalid(this.blankText);
21370                 return false;
21371              }
21372         }
21373         if(value.length < this.minLength){
21374             this.markInvalid(String.format(this.minLengthText, this.minLength));
21375             return false;
21376         }
21377         if(value.length > this.maxLength){
21378             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21379             return false;
21380         }
21381         if(this.vtype){
21382             var vt = Roo.form.VTypes;
21383             if(!vt[this.vtype](value, this)){
21384                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21385                 return false;
21386             }
21387         }
21388         if(typeof this.validator == "function"){
21389             var msg = this.validator(value);
21390             if(msg !== true){
21391                 this.markInvalid(msg);
21392                 return false;
21393             }
21394         }
21395         if(this.regex && !this.regex.test(value)){
21396             this.markInvalid(this.regexText);
21397             return false;
21398         }
21399         return true;
21400     },
21401
21402     /**
21403      * Selects text in this field
21404      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21405      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21406      */
21407     selectText : function(start, end){
21408         var v = this.getRawValue();
21409         if(v.length > 0){
21410             start = start === undefined ? 0 : start;
21411             end = end === undefined ? v.length : end;
21412             var d = this.el.dom;
21413             if(d.setSelectionRange){
21414                 d.setSelectionRange(start, end);
21415             }else if(d.createTextRange){
21416                 var range = d.createTextRange();
21417                 range.moveStart("character", start);
21418                 range.moveEnd("character", v.length-end);
21419                 range.select();
21420             }
21421         }
21422     },
21423
21424     /**
21425      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21426      * This only takes effect if grow = true, and fires the autosize event.
21427      */
21428     autoSize : function(){
21429         if(!this.grow || !this.rendered){
21430             return;
21431         }
21432         if(!this.metrics){
21433             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21434         }
21435         var el = this.el;
21436         var v = el.dom.value;
21437         var d = document.createElement('div');
21438         d.appendChild(document.createTextNode(v));
21439         v = d.innerHTML;
21440         d = null;
21441         v += "&#160;";
21442         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21443         this.el.setWidth(w);
21444         this.fireEvent("autosize", this, w);
21445     }
21446 });/*
21447  * Based on:
21448  * Ext JS Library 1.1.1
21449  * Copyright(c) 2006-2007, Ext JS, LLC.
21450  *
21451  * Originally Released Under LGPL - original licence link has changed is not relivant.
21452  *
21453  * Fork - LGPL
21454  * <script type="text/javascript">
21455  */
21456  
21457 /**
21458  * @class Roo.form.Hidden
21459  * @extends Roo.form.TextField
21460  * Simple Hidden element used on forms 
21461  * 
21462  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21463  * 
21464  * @constructor
21465  * Creates a new Hidden form element.
21466  * @param {Object} config Configuration options
21467  */
21468
21469
21470
21471 // easy hidden field...
21472 Roo.form.Hidden = function(config){
21473     Roo.form.Hidden.superclass.constructor.call(this, config);
21474 };
21475   
21476 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21477     fieldLabel:      '',
21478     inputType:      'hidden',
21479     width:          50,
21480     allowBlank:     true,
21481     labelSeparator: '',
21482     hidden:         true,
21483     itemCls :       'x-form-item-display-none'
21484
21485
21486 });
21487
21488
21489 /*
21490  * Based on:
21491  * Ext JS Library 1.1.1
21492  * Copyright(c) 2006-2007, Ext JS, LLC.
21493  *
21494  * Originally Released Under LGPL - original licence link has changed is not relivant.
21495  *
21496  * Fork - LGPL
21497  * <script type="text/javascript">
21498  */
21499  
21500 /**
21501  * @class Roo.form.TriggerField
21502  * @extends Roo.form.TextField
21503  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21504  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21505  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21506  * for which you can provide a custom implementation.  For example:
21507  * <pre><code>
21508 var trigger = new Roo.form.TriggerField();
21509 trigger.onTriggerClick = myTriggerFn;
21510 trigger.applyTo('my-field');
21511 </code></pre>
21512  *
21513  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21514  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21515  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21516  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21517  * @constructor
21518  * Create a new TriggerField.
21519  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21520  * to the base TextField)
21521  */
21522 Roo.form.TriggerField = function(config){
21523     this.mimicing = false;
21524     Roo.form.TriggerField.superclass.constructor.call(this, config);
21525 };
21526
21527 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21528     /**
21529      * @cfg {String} triggerClass A CSS class to apply to the trigger
21530      */
21531     /**
21532      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21533      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21534      */
21535     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21536     /**
21537      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21538      */
21539     hideTrigger:false,
21540
21541     /** @cfg {Boolean} grow @hide */
21542     /** @cfg {Number} growMin @hide */
21543     /** @cfg {Number} growMax @hide */
21544
21545     /**
21546      * @hide 
21547      * @method
21548      */
21549     autoSize: Roo.emptyFn,
21550     // private
21551     monitorTab : true,
21552     // private
21553     deferHeight : true,
21554
21555     
21556     actionMode : 'wrap',
21557     // private
21558     onResize : function(w, h){
21559         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21560         if(typeof w == 'number'){
21561             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
21562         }
21563     },
21564
21565     // private
21566     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21567
21568     // private
21569     getResizeEl : function(){
21570         return this.wrap;
21571     },
21572
21573     // private
21574     getPositionEl : function(){
21575         return this.wrap;
21576     },
21577
21578     // private
21579     alignErrorIcon : function(){
21580         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21581     },
21582
21583     // private
21584     onRender : function(ct, position){
21585         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21586         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21587         this.trigger = this.wrap.createChild(this.triggerConfig ||
21588                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21589         if(this.hideTrigger){
21590             this.trigger.setDisplayed(false);
21591         }
21592         this.initTrigger();
21593         if(!this.width){
21594             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21595         }
21596     },
21597
21598     // private
21599     initTrigger : function(){
21600         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21601         this.trigger.addClassOnOver('x-form-trigger-over');
21602         this.trigger.addClassOnClick('x-form-trigger-click');
21603     },
21604
21605     // private
21606     onDestroy : function(){
21607         if(this.trigger){
21608             this.trigger.removeAllListeners();
21609             this.trigger.remove();
21610         }
21611         if(this.wrap){
21612             this.wrap.remove();
21613         }
21614         Roo.form.TriggerField.superclass.onDestroy.call(this);
21615     },
21616
21617     // private
21618     onFocus : function(){
21619         Roo.form.TriggerField.superclass.onFocus.call(this);
21620         if(!this.mimicing){
21621             this.wrap.addClass('x-trigger-wrap-focus');
21622             this.mimicing = true;
21623             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21624             if(this.monitorTab){
21625                 this.el.on("keydown", this.checkTab, this);
21626             }
21627         }
21628     },
21629
21630     // private
21631     checkTab : function(e){
21632         if(e.getKey() == e.TAB){
21633             this.triggerBlur();
21634         }
21635     },
21636
21637     // private
21638     onBlur : function(){
21639         // do nothing
21640     },
21641
21642     // private
21643     mimicBlur : function(e, t){
21644         if(!this.wrap.contains(t) && this.validateBlur()){
21645             this.triggerBlur();
21646         }
21647     },
21648
21649     // private
21650     triggerBlur : function(){
21651         this.mimicing = false;
21652         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21653         if(this.monitorTab){
21654             this.el.un("keydown", this.checkTab, this);
21655         }
21656         this.wrap.removeClass('x-trigger-wrap-focus');
21657         Roo.form.TriggerField.superclass.onBlur.call(this);
21658     },
21659
21660     // private
21661     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21662     validateBlur : function(e, t){
21663         return true;
21664     },
21665
21666     // private
21667     onDisable : function(){
21668         Roo.form.TriggerField.superclass.onDisable.call(this);
21669         if(this.wrap){
21670             this.wrap.addClass('x-item-disabled');
21671         }
21672     },
21673
21674     // private
21675     onEnable : function(){
21676         Roo.form.TriggerField.superclass.onEnable.call(this);
21677         if(this.wrap){
21678             this.wrap.removeClass('x-item-disabled');
21679         }
21680     },
21681
21682     // private
21683     onShow : function(){
21684         var ae = this.getActionEl();
21685         
21686         if(ae){
21687             ae.dom.style.display = '';
21688             ae.dom.style.visibility = 'visible';
21689         }
21690     },
21691
21692     // private
21693     
21694     onHide : function(){
21695         var ae = this.getActionEl();
21696         ae.dom.style.display = 'none';
21697     },
21698
21699     /**
21700      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21701      * by an implementing function.
21702      * @method
21703      * @param {EventObject} e
21704      */
21705     onTriggerClick : Roo.emptyFn
21706 });
21707
21708 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21709 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21710 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21711 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21712     initComponent : function(){
21713         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21714
21715         this.triggerConfig = {
21716             tag:'span', cls:'x-form-twin-triggers', cn:[
21717             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21718             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21719         ]};
21720     },
21721
21722     getTrigger : function(index){
21723         return this.triggers[index];
21724     },
21725
21726     initTrigger : function(){
21727         var ts = this.trigger.select('.x-form-trigger', true);
21728         this.wrap.setStyle('overflow', 'hidden');
21729         var triggerField = this;
21730         ts.each(function(t, all, index){
21731             t.hide = function(){
21732                 var w = triggerField.wrap.getWidth();
21733                 this.dom.style.display = 'none';
21734                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21735             };
21736             t.show = function(){
21737                 var w = triggerField.wrap.getWidth();
21738                 this.dom.style.display = '';
21739                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21740             };
21741             var triggerIndex = 'Trigger'+(index+1);
21742
21743             if(this['hide'+triggerIndex]){
21744                 t.dom.style.display = 'none';
21745             }
21746             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21747             t.addClassOnOver('x-form-trigger-over');
21748             t.addClassOnClick('x-form-trigger-click');
21749         }, this);
21750         this.triggers = ts.elements;
21751     },
21752
21753     onTrigger1Click : Roo.emptyFn,
21754     onTrigger2Click : Roo.emptyFn
21755 });/*
21756  * Based on:
21757  * Ext JS Library 1.1.1
21758  * Copyright(c) 2006-2007, Ext JS, LLC.
21759  *
21760  * Originally Released Under LGPL - original licence link has changed is not relivant.
21761  *
21762  * Fork - LGPL
21763  * <script type="text/javascript">
21764  */
21765  
21766 /**
21767  * @class Roo.form.TextArea
21768  * @extends Roo.form.TextField
21769  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21770  * support for auto-sizing.
21771  * @constructor
21772  * Creates a new TextArea
21773  * @param {Object} config Configuration options
21774  */
21775 Roo.form.TextArea = function(config){
21776     Roo.form.TextArea.superclass.constructor.call(this, config);
21777     // these are provided exchanges for backwards compat
21778     // minHeight/maxHeight were replaced by growMin/growMax to be
21779     // compatible with TextField growing config values
21780     if(this.minHeight !== undefined){
21781         this.growMin = this.minHeight;
21782     }
21783     if(this.maxHeight !== undefined){
21784         this.growMax = this.maxHeight;
21785     }
21786 };
21787
21788 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21789     /**
21790      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21791      */
21792     growMin : 60,
21793     /**
21794      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21795      */
21796     growMax: 1000,
21797     /**
21798      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21799      * in the field (equivalent to setting overflow: hidden, defaults to false)
21800      */
21801     preventScrollbars: false,
21802     /**
21803      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21804      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21805      */
21806
21807     // private
21808     onRender : function(ct, position){
21809         if(!this.el){
21810             this.defaultAutoCreate = {
21811                 tag: "textarea",
21812                 style:"width:300px;height:60px;",
21813                 autocomplete: "off"
21814             };
21815         }
21816         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21817         if(this.grow){
21818             this.textSizeEl = Roo.DomHelper.append(document.body, {
21819                 tag: "pre", cls: "x-form-grow-sizer"
21820             });
21821             if(this.preventScrollbars){
21822                 this.el.setStyle("overflow", "hidden");
21823             }
21824             this.el.setHeight(this.growMin);
21825         }
21826     },
21827
21828     onDestroy : function(){
21829         if(this.textSizeEl){
21830             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21831         }
21832         Roo.form.TextArea.superclass.onDestroy.call(this);
21833     },
21834
21835     // private
21836     onKeyUp : function(e){
21837         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21838             this.autoSize();
21839         }
21840     },
21841
21842     /**
21843      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21844      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21845      */
21846     autoSize : function(){
21847         if(!this.grow || !this.textSizeEl){
21848             return;
21849         }
21850         var el = this.el;
21851         var v = el.dom.value;
21852         var ts = this.textSizeEl;
21853
21854         ts.innerHTML = '';
21855         ts.appendChild(document.createTextNode(v));
21856         v = ts.innerHTML;
21857
21858         Roo.fly(ts).setWidth(this.el.getWidth());
21859         if(v.length < 1){
21860             v = "&#160;&#160;";
21861         }else{
21862             if(Roo.isIE){
21863                 v = v.replace(/\n/g, '<p>&#160;</p>');
21864             }
21865             v += "&#160;\n&#160;";
21866         }
21867         ts.innerHTML = v;
21868         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21869         if(h != this.lastHeight){
21870             this.lastHeight = h;
21871             this.el.setHeight(h);
21872             this.fireEvent("autosize", this, h);
21873         }
21874     }
21875 });/*
21876  * Based on:
21877  * Ext JS Library 1.1.1
21878  * Copyright(c) 2006-2007, Ext JS, LLC.
21879  *
21880  * Originally Released Under LGPL - original licence link has changed is not relivant.
21881  *
21882  * Fork - LGPL
21883  * <script type="text/javascript">
21884  */
21885  
21886
21887 /**
21888  * @class Roo.form.NumberField
21889  * @extends Roo.form.TextField
21890  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21891  * @constructor
21892  * Creates a new NumberField
21893  * @param {Object} config Configuration options
21894  */
21895 Roo.form.NumberField = function(config){
21896     Roo.form.NumberField.superclass.constructor.call(this, config);
21897 };
21898
21899 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21900     /**
21901      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21902      */
21903     fieldClass: "x-form-field x-form-num-field",
21904     /**
21905      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21906      */
21907     allowDecimals : true,
21908     /**
21909      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21910      */
21911     decimalSeparator : ".",
21912     /**
21913      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21914      */
21915     decimalPrecision : 2,
21916     /**
21917      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21918      */
21919     allowNegative : true,
21920     /**
21921      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21922      */
21923     minValue : Number.NEGATIVE_INFINITY,
21924     /**
21925      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21926      */
21927     maxValue : Number.MAX_VALUE,
21928     /**
21929      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21930      */
21931     minText : "The minimum value for this field is {0}",
21932     /**
21933      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21934      */
21935     maxText : "The maximum value for this field is {0}",
21936     /**
21937      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21938      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21939      */
21940     nanText : "{0} is not a valid number",
21941
21942     // private
21943     initEvents : function(){
21944         Roo.form.NumberField.superclass.initEvents.call(this);
21945         var allowed = "0123456789";
21946         if(this.allowDecimals){
21947             allowed += this.decimalSeparator;
21948         }
21949         if(this.allowNegative){
21950             allowed += "-";
21951         }
21952         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21953         var keyPress = function(e){
21954             var k = e.getKey();
21955             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21956                 return;
21957             }
21958             var c = e.getCharCode();
21959             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21960                 e.stopEvent();
21961             }
21962         };
21963         this.el.on("keypress", keyPress, this);
21964     },
21965
21966     // private
21967     validateValue : function(value){
21968         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21969             return false;
21970         }
21971         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21972              return true;
21973         }
21974         var num = this.parseValue(value);
21975         if(isNaN(num)){
21976             this.markInvalid(String.format(this.nanText, value));
21977             return false;
21978         }
21979         if(num < this.minValue){
21980             this.markInvalid(String.format(this.minText, this.minValue));
21981             return false;
21982         }
21983         if(num > this.maxValue){
21984             this.markInvalid(String.format(this.maxText, this.maxValue));
21985             return false;
21986         }
21987         return true;
21988     },
21989
21990     getValue : function(){
21991         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21992     },
21993
21994     // private
21995     parseValue : function(value){
21996         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21997         return isNaN(value) ? '' : value;
21998     },
21999
22000     // private
22001     fixPrecision : function(value){
22002         var nan = isNaN(value);
22003         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22004             return nan ? '' : value;
22005         }
22006         return parseFloat(value).toFixed(this.decimalPrecision);
22007     },
22008
22009     setValue : function(v){
22010         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22011     },
22012
22013     // private
22014     decimalPrecisionFcn : function(v){
22015         return Math.floor(v);
22016     },
22017
22018     beforeBlur : function(){
22019         var v = this.parseValue(this.getRawValue());
22020         if(v){
22021             this.setValue(this.fixPrecision(v));
22022         }
22023     }
22024 });/*
22025  * Based on:
22026  * Ext JS Library 1.1.1
22027  * Copyright(c) 2006-2007, Ext JS, LLC.
22028  *
22029  * Originally Released Under LGPL - original licence link has changed is not relivant.
22030  *
22031  * Fork - LGPL
22032  * <script type="text/javascript">
22033  */
22034  
22035 /**
22036  * @class Roo.form.DateField
22037  * @extends Roo.form.TriggerField
22038  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22039 * @constructor
22040 * Create a new DateField
22041 * @param {Object} config
22042  */
22043 Roo.form.DateField = function(config){
22044     Roo.form.DateField.superclass.constructor.call(this, config);
22045     
22046       this.addEvents({
22047          
22048         /**
22049          * @event select
22050          * Fires when a date is selected
22051              * @param {Roo.form.DateField} combo This combo box
22052              * @param {Date} date The date selected
22053              */
22054         'select' : true
22055          
22056     });
22057     
22058     
22059     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22060     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22061     this.ddMatch = null;
22062     if(this.disabledDates){
22063         var dd = this.disabledDates;
22064         var re = "(?:";
22065         for(var i = 0; i < dd.length; i++){
22066             re += dd[i];
22067             if(i != dd.length-1) re += "|";
22068         }
22069         this.ddMatch = new RegExp(re + ")");
22070     }
22071 };
22072
22073 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22074     /**
22075      * @cfg {String} format
22076      * The default date format string which can be overriden for localization support.  The format must be
22077      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22078      */
22079     format : "m/d/y",
22080     /**
22081      * @cfg {String} altFormats
22082      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22083      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22084      */
22085     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22086     /**
22087      * @cfg {Array} disabledDays
22088      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22089      */
22090     disabledDays : null,
22091     /**
22092      * @cfg {String} disabledDaysText
22093      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22094      */
22095     disabledDaysText : "Disabled",
22096     /**
22097      * @cfg {Array} disabledDates
22098      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22099      * expression so they are very powerful. Some examples:
22100      * <ul>
22101      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22102      * <li>["03/08", "09/16"] would disable those days for every year</li>
22103      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22104      * <li>["03/../2006"] would disable every day in March 2006</li>
22105      * <li>["^03"] would disable every day in every March</li>
22106      * </ul>
22107      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22108      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22109      */
22110     disabledDates : null,
22111     /**
22112      * @cfg {String} disabledDatesText
22113      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22114      */
22115     disabledDatesText : "Disabled",
22116     /**
22117      * @cfg {Date/String} minValue
22118      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22119      * valid format (defaults to null).
22120      */
22121     minValue : null,
22122     /**
22123      * @cfg {Date/String} maxValue
22124      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22125      * valid format (defaults to null).
22126      */
22127     maxValue : null,
22128     /**
22129      * @cfg {String} minText
22130      * The error text to display when the date in the cell is before minValue (defaults to
22131      * 'The date in this field must be after {minValue}').
22132      */
22133     minText : "The date in this field must be equal to or after {0}",
22134     /**
22135      * @cfg {String} maxText
22136      * The error text to display when the date in the cell is after maxValue (defaults to
22137      * 'The date in this field must be before {maxValue}').
22138      */
22139     maxText : "The date in this field must be equal to or before {0}",
22140     /**
22141      * @cfg {String} invalidText
22142      * The error text to display when the date in the field is invalid (defaults to
22143      * '{value} is not a valid date - it must be in the format {format}').
22144      */
22145     invalidText : "{0} is not a valid date - it must be in the format {1}",
22146     /**
22147      * @cfg {String} triggerClass
22148      * An additional CSS class used to style the trigger button.  The trigger will always get the
22149      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22150      * which displays a calendar icon).
22151      */
22152     triggerClass : 'x-form-date-trigger',
22153     
22154
22155     /**
22156      * @cfg {bool} useIso
22157      * if enabled, then the date field will use a hidden field to store the 
22158      * real value as iso formated date. default (false)
22159      */ 
22160     useIso : false,
22161     /**
22162      * @cfg {String/Object} autoCreate
22163      * A DomHelper element spec, or true for a default element spec (defaults to
22164      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22165      */ 
22166     // private
22167     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22168     
22169     // private
22170     hiddenField: false,
22171     
22172     onRender : function(ct, position)
22173     {
22174         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22175         if (this.useIso) {
22176             this.el.dom.removeAttribute('name'); 
22177             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22178                     'before', true);
22179             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22180             // prevent input submission
22181             this.hiddenName = this.name;
22182         }
22183             
22184             
22185     },
22186     
22187     // private
22188     validateValue : function(value)
22189     {
22190         value = this.formatDate(value);
22191         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22192             return false;
22193         }
22194         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22195              return true;
22196         }
22197         var svalue = value;
22198         value = this.parseDate(value);
22199         if(!value){
22200             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22201             return false;
22202         }
22203         var time = value.getTime();
22204         if(this.minValue && time < this.minValue.getTime()){
22205             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22206             return false;
22207         }
22208         if(this.maxValue && time > this.maxValue.getTime()){
22209             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22210             return false;
22211         }
22212         if(this.disabledDays){
22213             var day = value.getDay();
22214             for(var i = 0; i < this.disabledDays.length; i++) {
22215                 if(day === this.disabledDays[i]){
22216                     this.markInvalid(this.disabledDaysText);
22217                     return false;
22218                 }
22219             }
22220         }
22221         var fvalue = this.formatDate(value);
22222         if(this.ddMatch && this.ddMatch.test(fvalue)){
22223             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22224             return false;
22225         }
22226         return true;
22227     },
22228
22229     // private
22230     // Provides logic to override the default TriggerField.validateBlur which just returns true
22231     validateBlur : function(){
22232         return !this.menu || !this.menu.isVisible();
22233     },
22234
22235     /**
22236      * Returns the current date value of the date field.
22237      * @return {Date} The date value
22238      */
22239     getValue : function(){
22240         
22241         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22242     },
22243
22244     /**
22245      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22246      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22247      * (the default format used is "m/d/y").
22248      * <br />Usage:
22249      * <pre><code>
22250 //All of these calls set the same date value (May 4, 2006)
22251
22252 //Pass a date object:
22253 var dt = new Date('5/4/06');
22254 dateField.setValue(dt);
22255
22256 //Pass a date string (default format):
22257 dateField.setValue('5/4/06');
22258
22259 //Pass a date string (custom format):
22260 dateField.format = 'Y-m-d';
22261 dateField.setValue('2006-5-4');
22262 </code></pre>
22263      * @param {String/Date} date The date or valid date string
22264      */
22265     setValue : function(date){
22266         if (this.hiddenField) {
22267             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22268         }
22269         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22270     },
22271
22272     // private
22273     parseDate : function(value){
22274         if(!value || value instanceof Date){
22275             return value;
22276         }
22277         var v = Date.parseDate(value, this.format);
22278         if(!v && this.altFormats){
22279             if(!this.altFormatsArray){
22280                 this.altFormatsArray = this.altFormats.split("|");
22281             }
22282             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22283                 v = Date.parseDate(value, this.altFormatsArray[i]);
22284             }
22285         }
22286         return v;
22287     },
22288
22289     // private
22290     formatDate : function(date, fmt){
22291         return (!date || !(date instanceof Date)) ?
22292                date : date.dateFormat(fmt || this.format);
22293     },
22294
22295     // private
22296     menuListeners : {
22297         select: function(m, d){
22298             this.setValue(d);
22299             this.fireEvent('select', this, d);
22300         },
22301         show : function(){ // retain focus styling
22302             this.onFocus();
22303         },
22304         hide : function(){
22305             this.focus.defer(10, this);
22306             var ml = this.menuListeners;
22307             this.menu.un("select", ml.select,  this);
22308             this.menu.un("show", ml.show,  this);
22309             this.menu.un("hide", ml.hide,  this);
22310         }
22311     },
22312
22313     // private
22314     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22315     onTriggerClick : function(){
22316         if(this.disabled){
22317             return;
22318         }
22319         if(this.menu == null){
22320             this.menu = new Roo.menu.DateMenu();
22321         }
22322         Roo.apply(this.menu.picker,  {
22323             showClear: this.allowBlank,
22324             minDate : this.minValue,
22325             maxDate : this.maxValue,
22326             disabledDatesRE : this.ddMatch,
22327             disabledDatesText : this.disabledDatesText,
22328             disabledDays : this.disabledDays,
22329             disabledDaysText : this.disabledDaysText,
22330             format : this.format,
22331             minText : String.format(this.minText, this.formatDate(this.minValue)),
22332             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22333         });
22334         this.menu.on(Roo.apply({}, this.menuListeners, {
22335             scope:this
22336         }));
22337         this.menu.picker.setValue(this.getValue() || new Date());
22338         this.menu.show(this.el, "tl-bl?");
22339     },
22340
22341     beforeBlur : function(){
22342         var v = this.parseDate(this.getRawValue());
22343         if(v){
22344             this.setValue(v);
22345         }
22346     }
22347
22348     /** @cfg {Boolean} grow @hide */
22349     /** @cfg {Number} growMin @hide */
22350     /** @cfg {Number} growMax @hide */
22351     /**
22352      * @hide
22353      * @method autoSize
22354      */
22355 });/*
22356  * Based on:
22357  * Ext JS Library 1.1.1
22358  * Copyright(c) 2006-2007, Ext JS, LLC.
22359  *
22360  * Originally Released Under LGPL - original licence link has changed is not relivant.
22361  *
22362  * Fork - LGPL
22363  * <script type="text/javascript">
22364  */
22365  
22366
22367 /**
22368  * @class Roo.form.ComboBox
22369  * @extends Roo.form.TriggerField
22370  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22371  * @constructor
22372  * Create a new ComboBox.
22373  * @param {Object} config Configuration options
22374  */
22375 Roo.form.ComboBox = function(config){
22376     Roo.form.ComboBox.superclass.constructor.call(this, config);
22377     this.addEvents({
22378         /**
22379          * @event expand
22380          * Fires when the dropdown list is expanded
22381              * @param {Roo.form.ComboBox} combo This combo box
22382              */
22383         'expand' : true,
22384         /**
22385          * @event collapse
22386          * Fires when the dropdown list is collapsed
22387              * @param {Roo.form.ComboBox} combo This combo box
22388              */
22389         'collapse' : true,
22390         /**
22391          * @event beforeselect
22392          * Fires before a list item is selected. Return false to cancel the selection.
22393              * @param {Roo.form.ComboBox} combo This combo box
22394              * @param {Roo.data.Record} record The data record returned from the underlying store
22395              * @param {Number} index The index of the selected item in the dropdown list
22396              */
22397         'beforeselect' : true,
22398         /**
22399          * @event select
22400          * Fires when a list item is selected
22401              * @param {Roo.form.ComboBox} combo This combo box
22402              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22403              * @param {Number} index The index of the selected item in the dropdown list
22404              */
22405         'select' : true,
22406         /**
22407          * @event beforequery
22408          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22409          * The event object passed has these properties:
22410              * @param {Roo.form.ComboBox} combo This combo box
22411              * @param {String} query The query
22412              * @param {Boolean} forceAll true to force "all" query
22413              * @param {Boolean} cancel true to cancel the query
22414              * @param {Object} e The query event object
22415              */
22416         'beforequery': true
22417     });
22418     if(this.transform){
22419         this.allowDomMove = false;
22420         var s = Roo.getDom(this.transform);
22421         if(!this.hiddenName){
22422             this.hiddenName = s.name;
22423         }
22424         if(!this.store){
22425             this.mode = 'local';
22426             var d = [], opts = s.options;
22427             for(var i = 0, len = opts.length;i < len; i++){
22428                 var o = opts[i];
22429                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22430                 if(o.selected) {
22431                     this.value = value;
22432                 }
22433                 d.push([value, o.text]);
22434             }
22435             this.store = new Roo.data.SimpleStore({
22436                 'id': 0,
22437                 fields: ['value', 'text'],
22438                 data : d
22439             });
22440             this.valueField = 'value';
22441             this.displayField = 'text';
22442         }
22443         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22444         if(!this.lazyRender){
22445             this.target = true;
22446             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22447             s.parentNode.removeChild(s); // remove it
22448             this.render(this.el.parentNode);
22449         }else{
22450             s.parentNode.removeChild(s); // remove it
22451         }
22452
22453     }
22454     if (this.store) {
22455         this.store = Roo.factory(this.store, Roo.data);
22456     }
22457     
22458     this.selectedIndex = -1;
22459     if(this.mode == 'local'){
22460         if(config.queryDelay === undefined){
22461             this.queryDelay = 10;
22462         }
22463         if(config.minChars === undefined){
22464             this.minChars = 0;
22465         }
22466     }
22467 };
22468
22469 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22470     /**
22471      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22472      */
22473     /**
22474      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22475      * rendering into an Roo.Editor, defaults to false)
22476      */
22477     /**
22478      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22479      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22480      */
22481     /**
22482      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22483      */
22484     /**
22485      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22486      * the dropdown list (defaults to undefined, with no header element)
22487      */
22488
22489      /**
22490      * @cfg {String/Roo.Template} tpl The template to use to render the output
22491      */
22492      
22493     // private
22494     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22495     /**
22496      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22497      */
22498     listWidth: undefined,
22499     /**
22500      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22501      * mode = 'remote' or 'text' if mode = 'local')
22502      */
22503     displayField: undefined,
22504     /**
22505      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22506      * mode = 'remote' or 'value' if mode = 'local'). 
22507      * Note: use of a valueField requires the user make a selection
22508      * in order for a value to be mapped.
22509      */
22510     valueField: undefined,
22511     /**
22512      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22513      * field's data value (defaults to the underlying DOM element's name)
22514      */
22515     hiddenName: undefined,
22516     /**
22517      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22518      */
22519     listClass: '',
22520     /**
22521      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22522      */
22523     selectedClass: 'x-combo-selected',
22524     /**
22525      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22526      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22527      * which displays a downward arrow icon).
22528      */
22529     triggerClass : 'x-form-arrow-trigger',
22530     /**
22531      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22532      */
22533     shadow:'sides',
22534     /**
22535      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22536      * anchor positions (defaults to 'tl-bl')
22537      */
22538     listAlign: 'tl-bl?',
22539     /**
22540      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22541      */
22542     maxHeight: 300,
22543     /**
22544      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22545      * query specified by the allQuery config option (defaults to 'query')
22546      */
22547     triggerAction: 'query',
22548     /**
22549      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22550      * (defaults to 4, does not apply if editable = false)
22551      */
22552     minChars : 4,
22553     /**
22554      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22555      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22556      */
22557     typeAhead: false,
22558     /**
22559      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22560      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22561      */
22562     queryDelay: 500,
22563     /**
22564      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22565      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22566      */
22567     pageSize: 0,
22568     /**
22569      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22570      * when editable = true (defaults to false)
22571      */
22572     selectOnFocus:false,
22573     /**
22574      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22575      */
22576     queryParam: 'query',
22577     /**
22578      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22579      * when mode = 'remote' (defaults to 'Loading...')
22580      */
22581     loadingText: 'Loading...',
22582     /**
22583      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22584      */
22585     resizable: false,
22586     /**
22587      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22588      */
22589     handleHeight : 8,
22590     /**
22591      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22592      * traditional select (defaults to true)
22593      */
22594     editable: true,
22595     /**
22596      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22597      */
22598     allQuery: '',
22599     /**
22600      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22601      */
22602     mode: 'remote',
22603     /**
22604      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22605      * listWidth has a higher value)
22606      */
22607     minListWidth : 70,
22608     /**
22609      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22610      * allow the user to set arbitrary text into the field (defaults to false)
22611      */
22612     forceSelection:false,
22613     /**
22614      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22615      * if typeAhead = true (defaults to 250)
22616      */
22617     typeAheadDelay : 250,
22618     /**
22619      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22620      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22621      */
22622     valueNotFoundText : undefined,
22623     /**
22624      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22625      */
22626     blockFocus : false,
22627     
22628     /**
22629      * @cfg {bool} disableClear Disable showing of clear button.
22630      */
22631     disableClear : false,
22632     
22633     // private
22634     onRender : function(ct, position){
22635         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22636         if(this.hiddenName){
22637             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22638                     'before', true);
22639             this.hiddenField.value =
22640                 this.hiddenValue !== undefined ? this.hiddenValue :
22641                 this.value !== undefined ? this.value : '';
22642
22643             // prevent input submission
22644             this.el.dom.removeAttribute('name');
22645         }
22646         if(Roo.isGecko){
22647             this.el.dom.setAttribute('autocomplete', 'off');
22648         }
22649
22650         var cls = 'x-combo-list';
22651
22652         this.list = new Roo.Layer({
22653             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22654         });
22655
22656         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22657         this.list.setWidth(lw);
22658         this.list.swallowEvent('mousewheel');
22659         this.assetHeight = 0;
22660
22661         if(this.title){
22662             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22663             this.assetHeight += this.header.getHeight();
22664         }
22665
22666         this.innerList = this.list.createChild({cls:cls+'-inner'});
22667         this.innerList.on('mouseover', this.onViewOver, this);
22668         this.innerList.on('mousemove', this.onViewMove, this);
22669         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22670         
22671         if(this.allowBlank && !this.pageSize && !this.disableClear){
22672             this.footer = this.list.createChild({cls:cls+'-ft'});
22673             this.pageTb = new Roo.Toolbar(this.footer);
22674            
22675         }
22676         if(this.pageSize){
22677             this.footer = this.list.createChild({cls:cls+'-ft'});
22678             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22679                     {pageSize: this.pageSize});
22680             
22681         }
22682         
22683         if (this.pageTb && this.allowBlank && !this.disableClear) {
22684             var _this = this;
22685             this.pageTb.add(new Roo.Toolbar.Fill(), {
22686                 cls: 'x-btn-icon x-btn-clear',
22687                 text: '&#160;',
22688                 handler: function()
22689                 {
22690                     _this.collapse();
22691                     _this.clearValue();
22692                     _this.onSelect(false, -1);
22693                 }
22694             });
22695         }
22696         if (this.footer) {
22697             this.assetHeight += this.footer.getHeight();
22698         }
22699         
22700
22701         if(!this.tpl){
22702             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22703         }
22704
22705         this.view = new Roo.View(this.innerList, this.tpl, {
22706             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22707         });
22708
22709         this.view.on('click', this.onViewClick, this);
22710
22711         this.store.on('beforeload', this.onBeforeLoad, this);
22712         this.store.on('load', this.onLoad, this);
22713         this.store.on('loadexception', this.collapse, this);
22714
22715         if(this.resizable){
22716             this.resizer = new Roo.Resizable(this.list,  {
22717                pinned:true, handles:'se'
22718             });
22719             this.resizer.on('resize', function(r, w, h){
22720                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22721                 this.listWidth = w;
22722                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22723                 this.restrictHeight();
22724             }, this);
22725             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22726         }
22727         if(!this.editable){
22728             this.editable = true;
22729             this.setEditable(false);
22730         }
22731     },
22732
22733     // private
22734     initEvents : function(){
22735         Roo.form.ComboBox.superclass.initEvents.call(this);
22736
22737         this.keyNav = new Roo.KeyNav(this.el, {
22738             "up" : function(e){
22739                 this.inKeyMode = true;
22740                 this.selectPrev();
22741             },
22742
22743             "down" : function(e){
22744                 if(!this.isExpanded()){
22745                     this.onTriggerClick();
22746                 }else{
22747                     this.inKeyMode = true;
22748                     this.selectNext();
22749                 }
22750             },
22751
22752             "enter" : function(e){
22753                 this.onViewClick();
22754                 //return true;
22755             },
22756
22757             "esc" : function(e){
22758                 this.collapse();
22759             },
22760
22761             "tab" : function(e){
22762                 this.onViewClick(false);
22763                 return true;
22764             },
22765
22766             scope : this,
22767
22768             doRelay : function(foo, bar, hname){
22769                 if(hname == 'down' || this.scope.isExpanded()){
22770                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22771                 }
22772                 return true;
22773             },
22774
22775             forceKeyDown: true
22776         });
22777         this.queryDelay = Math.max(this.queryDelay || 10,
22778                 this.mode == 'local' ? 10 : 250);
22779         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
22780         if(this.typeAhead){
22781             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
22782         }
22783         if(this.editable !== false){
22784             this.el.on("keyup", this.onKeyUp, this);
22785         }
22786         if(this.forceSelection){
22787             this.on('blur', this.doForce, this);
22788         }
22789     },
22790
22791     onDestroy : function(){
22792         if(this.view){
22793             this.view.setStore(null);
22794             this.view.el.removeAllListeners();
22795             this.view.el.remove();
22796             this.view.purgeListeners();
22797         }
22798         if(this.list){
22799             this.list.destroy();
22800         }
22801         if(this.store){
22802             this.store.un('beforeload', this.onBeforeLoad, this);
22803             this.store.un('load', this.onLoad, this);
22804             this.store.un('loadexception', this.collapse, this);
22805         }
22806         Roo.form.ComboBox.superclass.onDestroy.call(this);
22807     },
22808
22809     // private
22810     fireKey : function(e){
22811         if(e.isNavKeyPress() && !this.list.isVisible()){
22812             this.fireEvent("specialkey", this, e);
22813         }
22814     },
22815
22816     // private
22817     onResize: function(w, h){
22818         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
22819         if(this.list && this.listWidth === undefined){
22820             var lw = Math.max(w, this.minListWidth);
22821             this.list.setWidth(lw);
22822             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22823         }
22824     },
22825
22826     /**
22827      * Allow or prevent the user from directly editing the field text.  If false is passed,
22828      * the user will only be able to select from the items defined in the dropdown list.  This method
22829      * is the runtime equivalent of setting the 'editable' config option at config time.
22830      * @param {Boolean} value True to allow the user to directly edit the field text
22831      */
22832     setEditable : function(value){
22833         if(value == this.editable){
22834             return;
22835         }
22836         this.editable = value;
22837         if(!value){
22838             this.el.dom.setAttribute('readOnly', true);
22839             this.el.on('mousedown', this.onTriggerClick,  this);
22840             this.el.addClass('x-combo-noedit');
22841         }else{
22842             this.el.dom.setAttribute('readOnly', false);
22843             this.el.un('mousedown', this.onTriggerClick,  this);
22844             this.el.removeClass('x-combo-noedit');
22845         }
22846     },
22847
22848     // private
22849     onBeforeLoad : function(){
22850         if(!this.hasFocus){
22851             return;
22852         }
22853         this.innerList.update(this.loadingText ?
22854                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
22855         this.restrictHeight();
22856         this.selectedIndex = -1;
22857     },
22858
22859     // private
22860     onLoad : function(){
22861         if(!this.hasFocus){
22862             return;
22863         }
22864         if(this.store.getCount() > 0){
22865             this.expand();
22866             this.restrictHeight();
22867             if(this.lastQuery == this.allQuery){
22868                 if(this.editable){
22869                     this.el.dom.select();
22870                 }
22871                 if(!this.selectByValue(this.value, true)){
22872                     this.select(0, true);
22873                 }
22874             }else{
22875                 this.selectNext();
22876                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
22877                     this.taTask.delay(this.typeAheadDelay);
22878                 }
22879             }
22880         }else{
22881             this.onEmptyResults();
22882         }
22883         //this.el.focus();
22884     },
22885
22886     // private
22887     onTypeAhead : function(){
22888         if(this.store.getCount() > 0){
22889             var r = this.store.getAt(0);
22890             var newValue = r.data[this.displayField];
22891             var len = newValue.length;
22892             var selStart = this.getRawValue().length;
22893             if(selStart != len){
22894                 this.setRawValue(newValue);
22895                 this.selectText(selStart, newValue.length);
22896             }
22897         }
22898     },
22899
22900     // private
22901     onSelect : function(record, index){
22902         if(this.fireEvent('beforeselect', this, record, index) !== false){
22903             this.setFromData(index > -1 ? record.data : false);
22904             this.collapse();
22905             this.fireEvent('select', this, record, index);
22906         }
22907     },
22908
22909     /**
22910      * Returns the currently selected field value or empty string if no value is set.
22911      * @return {String} value The selected value
22912      */
22913     getValue : function(){
22914         if(this.valueField){
22915             return typeof this.value != 'undefined' ? this.value : '';
22916         }else{
22917             return Roo.form.ComboBox.superclass.getValue.call(this);
22918         }
22919     },
22920
22921     /**
22922      * Clears any text/value currently set in the field
22923      */
22924     clearValue : function(){
22925         if(this.hiddenField){
22926             this.hiddenField.value = '';
22927         }
22928         this.value = '';
22929         this.setRawValue('');
22930         this.lastSelectionText = '';
22931         this.applyEmptyText();
22932     },
22933
22934     /**
22935      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
22936      * will be displayed in the field.  If the value does not match the data value of an existing item,
22937      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
22938      * Otherwise the field will be blank (although the value will still be set).
22939      * @param {String} value The value to match
22940      */
22941     setValue : function(v){
22942         var text = v;
22943         if(this.valueField){
22944             var r = this.findRecord(this.valueField, v);
22945             if(r){
22946                 text = r.data[this.displayField];
22947             }else if(this.valueNotFoundText !== undefined){
22948                 text = this.valueNotFoundText;
22949             }
22950         }
22951         this.lastSelectionText = text;
22952         if(this.hiddenField){
22953             this.hiddenField.value = v;
22954         }
22955         Roo.form.ComboBox.superclass.setValue.call(this, text);
22956         this.value = v;
22957     },
22958     /**
22959      * @property {Object} the last set data for the element
22960      */
22961     
22962     lastData : false,
22963     /**
22964      * Sets the value of the field based on a object which is related to the record format for the store.
22965      * @param {Object} value the value to set as. or false on reset?
22966      */
22967     setFromData : function(o){
22968         var dv = ''; // display value
22969         var vv = ''; // value value..
22970         this.lastData = o;
22971         if (this.displayField) {
22972             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
22973         } else {
22974             // this is an error condition!!!
22975             console.log('no value field set for '+ this.name);
22976         }
22977         
22978         if(this.valueField){
22979             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
22980         }
22981         if(this.hiddenField){
22982             this.hiddenField.value = vv;
22983             
22984             this.lastSelectionText = dv;
22985             Roo.form.ComboBox.superclass.setValue.call(this, dv);
22986             this.value = vv;
22987             return;
22988         }
22989         // no hidden field.. - we store the value in 'value', but still display
22990         // display field!!!!
22991         this.lastSelectionText = dv;
22992         Roo.form.ComboBox.superclass.setValue.call(this, dv);
22993         this.value = vv;
22994         
22995         
22996     },
22997     // private
22998     reset : function(){
22999         // overridden so that last data is reset..
23000         this.setValue(this.originalValue);
23001         this.clearInvalid();
23002         this.lastData = false;
23003     },
23004     // private
23005     findRecord : function(prop, value){
23006         var record;
23007         if(this.store.getCount() > 0){
23008             this.store.each(function(r){
23009                 if(r.data[prop] == value){
23010                     record = r;
23011                     return false;
23012                 }
23013             });
23014         }
23015         return record;
23016     },
23017
23018     // private
23019     onViewMove : function(e, t){
23020         this.inKeyMode = false;
23021     },
23022
23023     // private
23024     onViewOver : function(e, t){
23025         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23026             return;
23027         }
23028         var item = this.view.findItemFromChild(t);
23029         if(item){
23030             var index = this.view.indexOf(item);
23031             this.select(index, false);
23032         }
23033     },
23034
23035     // private
23036     onViewClick : function(doFocus){
23037         var index = this.view.getSelectedIndexes()[0];
23038         var r = this.store.getAt(index);
23039         if(r){
23040             this.onSelect(r, index);
23041         }
23042         if(doFocus !== false && !this.blockFocus){
23043             this.el.focus();
23044         }
23045     },
23046
23047     // private
23048     restrictHeight : function(){
23049         this.innerList.dom.style.height = '';
23050         var inner = this.innerList.dom;
23051         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23052         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23053         this.list.beginUpdate();
23054         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23055         this.list.alignTo(this.el, this.listAlign);
23056         this.list.endUpdate();
23057     },
23058
23059     // private
23060     onEmptyResults : function(){
23061         this.collapse();
23062     },
23063
23064     /**
23065      * Returns true if the dropdown list is expanded, else false.
23066      */
23067     isExpanded : function(){
23068         return this.list.isVisible();
23069     },
23070
23071     /**
23072      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23073      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23074      * @param {String} value The data value of the item to select
23075      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23076      * selected item if it is not currently in view (defaults to true)
23077      * @return {Boolean} True if the value matched an item in the list, else false
23078      */
23079     selectByValue : function(v, scrollIntoView){
23080         if(v !== undefined && v !== null){
23081             var r = this.findRecord(this.valueField || this.displayField, v);
23082             if(r){
23083                 this.select(this.store.indexOf(r), scrollIntoView);
23084                 return true;
23085             }
23086         }
23087         return false;
23088     },
23089
23090     /**
23091      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23092      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23093      * @param {Number} index The zero-based index of the list item to select
23094      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23095      * selected item if it is not currently in view (defaults to true)
23096      */
23097     select : function(index, scrollIntoView){
23098         this.selectedIndex = index;
23099         this.view.select(index);
23100         if(scrollIntoView !== false){
23101             var el = this.view.getNode(index);
23102             if(el){
23103                 this.innerList.scrollChildIntoView(el, false);
23104             }
23105         }
23106     },
23107
23108     // private
23109     selectNext : function(){
23110         var ct = this.store.getCount();
23111         if(ct > 0){
23112             if(this.selectedIndex == -1){
23113                 this.select(0);
23114             }else if(this.selectedIndex < ct-1){
23115                 this.select(this.selectedIndex+1);
23116             }
23117         }
23118     },
23119
23120     // private
23121     selectPrev : function(){
23122         var ct = this.store.getCount();
23123         if(ct > 0){
23124             if(this.selectedIndex == -1){
23125                 this.select(0);
23126             }else if(this.selectedIndex != 0){
23127                 this.select(this.selectedIndex-1);
23128             }
23129         }
23130     },
23131
23132     // private
23133     onKeyUp : function(e){
23134         if(this.editable !== false && !e.isSpecialKey()){
23135             this.lastKey = e.getKey();
23136             this.dqTask.delay(this.queryDelay);
23137         }
23138     },
23139
23140     // private
23141     validateBlur : function(){
23142         return !this.list || !this.list.isVisible();   
23143     },
23144
23145     // private
23146     initQuery : function(){
23147         this.doQuery(this.getRawValue());
23148     },
23149
23150     // private
23151     doForce : function(){
23152         if(this.el.dom.value.length > 0){
23153             this.el.dom.value =
23154                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23155             this.applyEmptyText();
23156         }
23157     },
23158
23159     /**
23160      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23161      * query allowing the query action to be canceled if needed.
23162      * @param {String} query The SQL query to execute
23163      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23164      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23165      * saved in the current store (defaults to false)
23166      */
23167     doQuery : function(q, forceAll){
23168         if(q === undefined || q === null){
23169             q = '';
23170         }
23171         var qe = {
23172             query: q,
23173             forceAll: forceAll,
23174             combo: this,
23175             cancel:false
23176         };
23177         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23178             return false;
23179         }
23180         q = qe.query;
23181         forceAll = qe.forceAll;
23182         if(forceAll === true || (q.length >= this.minChars)){
23183             if(this.lastQuery != q){
23184                 this.lastQuery = q;
23185                 if(this.mode == 'local'){
23186                     this.selectedIndex = -1;
23187                     if(forceAll){
23188                         this.store.clearFilter();
23189                     }else{
23190                         this.store.filter(this.displayField, q);
23191                     }
23192                     this.onLoad();
23193                 }else{
23194                     this.store.baseParams[this.queryParam] = q;
23195                     this.store.load({
23196                         params: this.getParams(q)
23197                     });
23198                     this.expand();
23199                 }
23200             }else{
23201                 this.selectedIndex = -1;
23202                 this.onLoad();   
23203             }
23204         }
23205     },
23206
23207     // private
23208     getParams : function(q){
23209         var p = {};
23210         //p[this.queryParam] = q;
23211         if(this.pageSize){
23212             p.start = 0;
23213             p.limit = this.pageSize;
23214         }
23215         return p;
23216     },
23217
23218     /**
23219      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23220      */
23221     collapse : function(){
23222         if(!this.isExpanded()){
23223             return;
23224         }
23225         this.list.hide();
23226         Roo.get(document).un('mousedown', this.collapseIf, this);
23227         Roo.get(document).un('mousewheel', this.collapseIf, this);
23228         this.fireEvent('collapse', this);
23229     },
23230
23231     // private
23232     collapseIf : function(e){
23233         if(!e.within(this.wrap) && !e.within(this.list)){
23234             this.collapse();
23235         }
23236     },
23237
23238     /**
23239      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23240      */
23241     expand : function(){
23242         if(this.isExpanded() || !this.hasFocus){
23243             return;
23244         }
23245         this.list.alignTo(this.el, this.listAlign);
23246         this.list.show();
23247         Roo.get(document).on('mousedown', this.collapseIf, this);
23248         Roo.get(document).on('mousewheel', this.collapseIf, this);
23249         this.fireEvent('expand', this);
23250     },
23251
23252     // private
23253     // Implements the default empty TriggerField.onTriggerClick function
23254     onTriggerClick : function(){
23255         if(this.disabled){
23256             return;
23257         }
23258         if(this.isExpanded()){
23259             this.collapse();
23260             if (!this.blockFocus) {
23261                 this.el.focus();
23262             }
23263             
23264         }else {
23265             this.hasFocus = true;
23266             if(this.triggerAction == 'all') {
23267                 this.doQuery(this.allQuery, true);
23268             } else {
23269                 this.doQuery(this.getRawValue());
23270             }
23271             if (!this.blockFocus) {
23272                 this.el.focus();
23273             }
23274         }
23275     }
23276
23277     /** 
23278     * @cfg {Boolean} grow 
23279     * @hide 
23280     */
23281     /** 
23282     * @cfg {Number} growMin 
23283     * @hide 
23284     */
23285     /** 
23286     * @cfg {Number} growMax 
23287     * @hide 
23288     */
23289     /**
23290      * @hide
23291      * @method autoSize
23292      */
23293 });/*
23294  * Based on:
23295  * Ext JS Library 1.1.1
23296  * Copyright(c) 2006-2007, Ext JS, LLC.
23297  *
23298  * Originally Released Under LGPL - original licence link has changed is not relivant.
23299  *
23300  * Fork - LGPL
23301  * <script type="text/javascript">
23302  */
23303 /**
23304  * @class Roo.form.Checkbox
23305  * @extends Roo.form.Field
23306  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23307  * @constructor
23308  * Creates a new Checkbox
23309  * @param {Object} config Configuration options
23310  */
23311 Roo.form.Checkbox = function(config){
23312     Roo.form.Checkbox.superclass.constructor.call(this, config);
23313     this.addEvents({
23314         /**
23315          * @event check
23316          * Fires when the checkbox is checked or unchecked.
23317              * @param {Roo.form.Checkbox} this This checkbox
23318              * @param {Boolean} checked The new checked value
23319              */
23320         check : true
23321     });
23322 };
23323
23324 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23325     /**
23326      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23327      */
23328     focusClass : undefined,
23329     /**
23330      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23331      */
23332     fieldClass: "x-form-field",
23333     /**
23334      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23335      */
23336     checked: false,
23337     /**
23338      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23339      * {tag: "input", type: "checkbox", autocomplete: "off"})
23340      */
23341     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23342     /**
23343      * @cfg {String} boxLabel The text that appears beside the checkbox
23344      */
23345     boxLabel : "",
23346     /**
23347      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23348      */  
23349     inputValue : '1',
23350     /**
23351      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23352      */
23353      valueOff: '0', // value when not checked..
23354
23355     actionMode : 'viewEl', 
23356     //
23357     // private
23358     itemCls : 'x-menu-check-item x-form-item',
23359     groupClass : 'x-menu-group-item',
23360     inputType : 'hidden',
23361     
23362     
23363     inSetChecked: false, // check that we are not calling self...
23364     
23365     inputElement: false, // real input element?
23366     basedOn: false, // ????
23367     
23368     isFormField: true, // not sure where this is needed!!!!
23369
23370     onResize : function(){
23371         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23372         if(!this.boxLabel){
23373             this.el.alignTo(this.wrap, 'c-c');
23374         }
23375     },
23376
23377     initEvents : function(){
23378         Roo.form.Checkbox.superclass.initEvents.call(this);
23379         this.el.on("click", this.onClick,  this);
23380         this.el.on("change", this.onClick,  this);
23381     },
23382
23383
23384     getResizeEl : function(){
23385         return this.wrap;
23386     },
23387
23388     getPositionEl : function(){
23389         return this.wrap;
23390     },
23391
23392     // private
23393     onRender : function(ct, position){
23394         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23395         /*
23396         if(this.inputValue !== undefined){
23397             this.el.dom.value = this.inputValue;
23398         }
23399         */
23400         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23401         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23402         var viewEl = this.wrap.createChild({ 
23403             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23404         this.viewEl = viewEl;   
23405         this.wrap.on('click', this.onClick,  this); 
23406         
23407         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23408         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23409         
23410         
23411         
23412         if(this.boxLabel){
23413             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23414         //    viewEl.on('click', this.onClick,  this); 
23415         }
23416         //if(this.checked){
23417             this.setChecked(this.checked);
23418         //}else{
23419             //this.checked = this.el.dom;
23420         //}
23421
23422     },
23423
23424     // private
23425     initValue : Roo.emptyFn,
23426
23427     /**
23428      * Returns the checked state of the checkbox.
23429      * @return {Boolean} True if checked, else false
23430      */
23431     getValue : function(){
23432         if(this.el){
23433             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23434         }
23435         return this.valueOff;
23436         
23437     },
23438
23439         // private
23440     onClick : function(){ 
23441         this.setChecked(!this.checked);
23442
23443         //if(this.el.dom.checked != this.checked){
23444         //    this.setValue(this.el.dom.checked);
23445        // }
23446     },
23447
23448     /**
23449      * Sets the checked state of the checkbox.
23450      * On is always based on a string comparison between inputValue and the param.
23451      * @param {Boolean/String} value - the value to set 
23452      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23453      */
23454     setValue : function(v,suppressEvent){
23455         
23456         
23457         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23458         //if(this.el && this.el.dom){
23459         //    this.el.dom.checked = this.checked;
23460         //    this.el.dom.defaultChecked = this.checked;
23461         //}
23462         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23463         //this.fireEvent("check", this, this.checked);
23464     },
23465     // private..
23466     setChecked : function(state,suppressEvent)
23467     {
23468         if (this.inSetChecked) {
23469             this.checked = state;
23470             return;
23471         }
23472         
23473     
23474         if(this.wrap){
23475             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23476         }
23477         this.checked = state;
23478         if(suppressEvent !== true){
23479             this.fireEvent('checkchange', this, state);
23480         }
23481         this.inSetChecked = true;
23482         this.el.dom.value = state ? this.inputValue : this.valueOff;
23483         this.inSetChecked = false;
23484         
23485     },
23486     // handle setting of hidden value by some other method!!?!?
23487     setFromHidden: function()
23488     {
23489         if(!this.el){
23490             return;
23491         }
23492         //console.log("SET FROM HIDDEN");
23493         //alert('setFrom hidden');
23494         this.setValue(this.el.dom.value);
23495     },
23496     
23497     onDestroy : function()
23498     {
23499         if(this.viewEl){
23500             Roo.get(this.viewEl).remove();
23501         }
23502          
23503         Roo.form.Checkbox.superclass.onDestroy.call(this);
23504     }
23505
23506 });/*
23507  * Based on:
23508  * Ext JS Library 1.1.1
23509  * Copyright(c) 2006-2007, Ext JS, LLC.
23510  *
23511  * Originally Released Under LGPL - original licence link has changed is not relivant.
23512  *
23513  * Fork - LGPL
23514  * <script type="text/javascript">
23515  */
23516  
23517 /**
23518  * @class Roo.form.Radio
23519  * @extends Roo.form.Checkbox
23520  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23521  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23522  * @constructor
23523  * Creates a new Radio
23524  * @param {Object} config Configuration options
23525  */
23526 Roo.form.Radio = function(){
23527     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23528 };
23529 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23530     inputType: 'radio',
23531
23532     /**
23533      * If this radio is part of a group, it will return the selected value
23534      * @return {String}
23535      */
23536     getGroupValue : function(){
23537         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23538     }
23539 });//<script type="text/javascript">
23540
23541 /*
23542  * Ext JS Library 1.1.1
23543  * Copyright(c) 2006-2007, Ext JS, LLC.
23544  * licensing@extjs.com
23545  * 
23546  * http://www.extjs.com/license
23547  */
23548  
23549  /*
23550   * 
23551   * Known bugs:
23552   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23553   * - IE ? - no idea how much works there.
23554   * 
23555   * 
23556   * 
23557   */
23558  
23559
23560 /**
23561  * @class Ext.form.HtmlEditor
23562  * @extends Ext.form.Field
23563  * Provides a lightweight HTML Editor component.
23564  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23565  * 
23566  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23567  * supported by this editor.</b><br/><br/>
23568  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23569  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23570  */
23571 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23572       /**
23573      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23574      */
23575     toolbars : false,
23576     /**
23577      * @cfg {String} createLinkText The default text for the create link prompt
23578      */
23579     createLinkText : 'Please enter the URL for the link:',
23580     /**
23581      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23582      */
23583     defaultLinkValue : 'http:/'+'/',
23584    
23585     
23586     // id of frame..
23587     frameId: false,
23588     
23589     // private properties
23590     validationEvent : false,
23591     deferHeight: true,
23592     initialized : false,
23593     activated : false,
23594     sourceEditMode : false,
23595     onFocus : Roo.emptyFn,
23596     iframePad:3,
23597     hideMode:'offsets',
23598     defaultAutoCreate : {
23599         tag: "textarea",
23600         style:"width:500px;height:300px;",
23601         autocomplete: "off"
23602     },
23603
23604     // private
23605     initComponent : function(){
23606         this.addEvents({
23607             /**
23608              * @event initialize
23609              * Fires when the editor is fully initialized (including the iframe)
23610              * @param {HtmlEditor} this
23611              */
23612             initialize: true,
23613             /**
23614              * @event activate
23615              * Fires when the editor is first receives the focus. Any insertion must wait
23616              * until after this event.
23617              * @param {HtmlEditor} this
23618              */
23619             activate: true,
23620              /**
23621              * @event beforesync
23622              * Fires before the textarea is updated with content from the editor iframe. Return false
23623              * to cancel the sync.
23624              * @param {HtmlEditor} this
23625              * @param {String} html
23626              */
23627             beforesync: true,
23628              /**
23629              * @event beforepush
23630              * Fires before the iframe editor is updated with content from the textarea. Return false
23631              * to cancel the push.
23632              * @param {HtmlEditor} this
23633              * @param {String} html
23634              */
23635             beforepush: true,
23636              /**
23637              * @event sync
23638              * Fires when the textarea is updated with content from the editor iframe.
23639              * @param {HtmlEditor} this
23640              * @param {String} html
23641              */
23642             sync: true,
23643              /**
23644              * @event push
23645              * Fires when the iframe editor is updated with content from the textarea.
23646              * @param {HtmlEditor} this
23647              * @param {String} html
23648              */
23649             push: true,
23650              /**
23651              * @event editmodechange
23652              * Fires when the editor switches edit modes
23653              * @param {HtmlEditor} this
23654              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23655              */
23656             editmodechange: true,
23657             /**
23658              * @event editorevent
23659              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23660              * @param {HtmlEditor} this
23661              */
23662             editorevent: true
23663         })
23664     },
23665
23666     /**
23667      * Protected method that will not generally be called directly. It
23668      * is called when the editor creates its toolbar. Override this method if you need to
23669      * add custom toolbar buttons.
23670      * @param {HtmlEditor} editor
23671      */
23672     createToolbar : function(editor){
23673         if (!editor.toolbars || !editor.toolbars.length) {
23674             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23675         }
23676         
23677         for (var i =0 ; i < editor.toolbars.length;i++) {
23678             editor.toolbars[i].init(editor);
23679         }
23680          
23681         
23682     },
23683
23684     /**
23685      * Protected method that will not generally be called directly. It
23686      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23687      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23688      */
23689     getDocMarkup : function(){
23690         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23691     },
23692
23693     // private
23694     onRender : function(ct, position){
23695         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23696         this.el.dom.style.border = '0 none';
23697         this.el.dom.setAttribute('tabIndex', -1);
23698         this.el.addClass('x-hidden');
23699         if(Roo.isIE){ // fix IE 1px bogus margin
23700             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23701         }
23702         this.wrap = this.el.wrap({
23703             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23704         });
23705
23706         this.frameId = Roo.id();
23707         this.createToolbar(this);
23708         
23709         
23710         
23711         
23712       
23713         
23714         var iframe = this.wrap.createChild({
23715             tag: 'iframe',
23716             id: this.frameId,
23717             name: this.frameId,
23718             frameBorder : 'no',
23719             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23720         });
23721         
23722        // console.log(iframe);
23723         //this.wrap.dom.appendChild(iframe);
23724
23725         this.iframe = iframe.dom;
23726
23727          this.assignDocWin();
23728         
23729         this.doc.designMode = 'on';
23730        
23731         this.doc.open();
23732         this.doc.write(this.getDocMarkup());
23733         this.doc.close();
23734
23735         
23736         var task = { // must defer to wait for browser to be ready
23737             run : function(){
23738                 //console.log("run task?" + this.doc.readyState);
23739                 this.assignDocWin();
23740                 if(this.doc.body || this.doc.readyState == 'complete'){
23741                     try {
23742                         
23743                        
23744                         this.doc.designMode="on";
23745                     } catch (e) {
23746                         return;
23747                     }
23748                     Roo.TaskMgr.stop(task);
23749                     this.initEditor.defer(10, this);
23750                 }
23751             },
23752             interval : 10,
23753             duration:10000,
23754             scope: this
23755         };
23756         Roo.TaskMgr.start(task);
23757
23758         if(!this.width){
23759             this.setSize(this.el.getSize());
23760         }
23761     },
23762
23763     // private
23764     onResize : function(w, h){
23765         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23766         if(this.el && this.iframe){
23767             if(typeof w == 'number'){
23768                 var aw = w - this.wrap.getFrameWidth('lr');
23769                 this.el.setWidth(this.adjustWidth('textarea', aw));
23770                 this.iframe.style.width = aw + 'px';
23771             }
23772             if(typeof h == 'number'){
23773                 var tbh = 0;
23774                 for (var i =0; i < this.toolbars.length;i++) {
23775                     // fixme - ask toolbars for heights?
23776                     tbh += this.toolbars[i].tb.el.getHeight();
23777                 }
23778                 
23779                 
23780                 
23781                 
23782                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23783                 this.el.setHeight(this.adjustWidth('textarea', ah));
23784                 this.iframe.style.height = ah + 'px';
23785                 if(this.doc){
23786                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
23787                 }
23788             }
23789         }
23790     },
23791
23792     /**
23793      * Toggles the editor between standard and source edit mode.
23794      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23795      */
23796     toggleSourceEdit : function(sourceEditMode){
23797         
23798         this.sourceEditMode = sourceEditMode === true;
23799         
23800         if(this.sourceEditMode){
23801           
23802             this.syncValue();
23803             this.iframe.className = 'x-hidden';
23804             this.el.removeClass('x-hidden');
23805             this.el.dom.removeAttribute('tabIndex');
23806             this.el.focus();
23807         }else{
23808              
23809             this.pushValue();
23810             this.iframe.className = '';
23811             this.el.addClass('x-hidden');
23812             this.el.dom.setAttribute('tabIndex', -1);
23813             this.deferFocus();
23814         }
23815         this.setSize(this.wrap.getSize());
23816         this.fireEvent('editmodechange', this, this.sourceEditMode);
23817     },
23818
23819     // private used internally
23820     createLink : function(){
23821         var url = prompt(this.createLinkText, this.defaultLinkValue);
23822         if(url && url != 'http:/'+'/'){
23823             this.relayCmd('createlink', url);
23824         }
23825     },
23826
23827     // private (for BoxComponent)
23828     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23829
23830     // private (for BoxComponent)
23831     getResizeEl : function(){
23832         return this.wrap;
23833     },
23834
23835     // private (for BoxComponent)
23836     getPositionEl : function(){
23837         return this.wrap;
23838     },
23839
23840     // private
23841     initEvents : function(){
23842         this.originalValue = this.getValue();
23843     },
23844
23845     /**
23846      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23847      * @method
23848      */
23849     markInvalid : Roo.emptyFn,
23850     /**
23851      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23852      * @method
23853      */
23854     clearInvalid : Roo.emptyFn,
23855
23856     setValue : function(v){
23857         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23858         this.pushValue();
23859     },
23860
23861     /**
23862      * Protected method that will not generally be called directly. If you need/want
23863      * custom HTML cleanup, this is the method you should override.
23864      * @param {String} html The HTML to be cleaned
23865      * return {String} The cleaned HTML
23866      */
23867     cleanHtml : function(html){
23868         html = String(html);
23869         if(html.length > 5){
23870             if(Roo.isSafari){ // strip safari nonsense
23871                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23872             }
23873         }
23874         if(html == '&nbsp;'){
23875             html = '';
23876         }
23877         return html;
23878     },
23879
23880     /**
23881      * Protected method that will not generally be called directly. Syncs the contents
23882      * of the editor iframe with the textarea.
23883      */
23884     syncValue : function(){
23885         if(this.initialized){
23886             var bd = (this.doc.body || this.doc.documentElement);
23887             var html = bd.innerHTML;
23888             if(Roo.isSafari){
23889                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23890                 var m = bs.match(/text-align:(.*?);/i);
23891                 if(m && m[1]){
23892                     html = '<div style="'+m[0]+'">' + html + '</div>';
23893                 }
23894             }
23895             html = this.cleanHtml(html);
23896             if(this.fireEvent('beforesync', this, html) !== false){
23897                 this.el.dom.value = html;
23898                 this.fireEvent('sync', this, html);
23899             }
23900         }
23901     },
23902
23903     /**
23904      * Protected method that will not generally be called directly. Pushes the value of the textarea
23905      * into the iframe editor.
23906      */
23907     pushValue : function(){
23908         if(this.initialized){
23909             var v = this.el.dom.value;
23910             if(v.length < 1){
23911                 v = '&#160;';
23912             }
23913             if(this.fireEvent('beforepush', this, v) !== false){
23914                 (this.doc.body || this.doc.documentElement).innerHTML = v;
23915                 this.fireEvent('push', this, v);
23916             }
23917         }
23918     },
23919
23920     // private
23921     deferFocus : function(){
23922         this.focus.defer(10, this);
23923     },
23924
23925     // doc'ed in Field
23926     focus : function(){
23927         if(this.win && !this.sourceEditMode){
23928             this.win.focus();
23929         }else{
23930             this.el.focus();
23931         }
23932     },
23933     
23934     assignDocWin: function()
23935     {
23936         var iframe = this.iframe;
23937         
23938          if(Roo.isIE){
23939             this.doc = iframe.contentWindow.document;
23940             this.win = iframe.contentWindow;
23941         } else {
23942             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23943             this.win = Roo.get(this.frameId).dom.contentWindow;
23944         }
23945     },
23946     
23947     // private
23948     initEditor : function(){
23949         //console.log("INIT EDITOR");
23950         this.assignDocWin();
23951         
23952         
23953         
23954         this.doc.designMode="on";
23955         this.doc.open();
23956         this.doc.write(this.getDocMarkup());
23957         this.doc.close();
23958         
23959         var dbody = (this.doc.body || this.doc.documentElement);
23960         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23961         // this copies styles from the containing element into thsi one..
23962         // not sure why we need all of this..
23963         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23964         ss['background-attachment'] = 'fixed'; // w3c
23965         dbody.bgProperties = 'fixed'; // ie
23966         Roo.DomHelper.applyStyles(dbody, ss);
23967         Roo.EventManager.on(this.doc, {
23968             'mousedown': this.onEditorEvent,
23969             'dblclick': this.onEditorEvent,
23970             'click': this.onEditorEvent,
23971             'keyup': this.onEditorEvent,
23972             buffer:100,
23973             scope: this
23974         });
23975         if(Roo.isGecko){
23976             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
23977         }
23978         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23979             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23980         }
23981         this.initialized = true;
23982
23983         this.fireEvent('initialize', this);
23984         this.pushValue();
23985     },
23986
23987     // private
23988     onDestroy : function(){
23989         
23990         
23991         
23992         if(this.rendered){
23993             
23994             for (var i =0; i < this.toolbars.length;i++) {
23995                 // fixme - ask toolbars for heights?
23996                 this.toolbars[i].onDestroy();
23997             }
23998             
23999             this.wrap.dom.innerHTML = '';
24000             this.wrap.remove();
24001         }
24002     },
24003
24004     // private
24005     onFirstFocus : function(){
24006         
24007         this.assignDocWin();
24008         
24009         
24010         this.activated = true;
24011         for (var i =0; i < this.toolbars.length;i++) {
24012             this.toolbars[i].onFirstFocus();
24013         }
24014        
24015         if(Roo.isGecko){ // prevent silly gecko errors
24016             this.win.focus();
24017             var s = this.win.getSelection();
24018             if(!s.focusNode || s.focusNode.nodeType != 3){
24019                 var r = s.getRangeAt(0);
24020                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24021                 r.collapse(true);
24022                 this.deferFocus();
24023             }
24024             try{
24025                 this.execCmd('useCSS', true);
24026                 this.execCmd('styleWithCSS', false);
24027             }catch(e){}
24028         }
24029         this.fireEvent('activate', this);
24030     },
24031
24032     // private
24033     adjustFont: function(btn){
24034         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24035         //if(Roo.isSafari){ // safari
24036         //    adjust *= 2;
24037        // }
24038         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24039         if(Roo.isSafari){ // safari
24040             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24041             v =  (v < 10) ? 10 : v;
24042             v =  (v > 48) ? 48 : v;
24043             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24044             
24045         }
24046         
24047         
24048         v = Math.max(1, v+adjust);
24049         
24050         this.execCmd('FontSize', v  );
24051     },
24052
24053     onEditorEvent : function(e){
24054         this.fireEvent('editorevent', this, e);
24055       //  this.updateToolbar();
24056         this.syncValue();
24057     },
24058
24059     insertTag : function(tg)
24060     {
24061         // could be a bit smarter... -> wrap the current selected tRoo..
24062         
24063         this.execCmd("formatblock",   tg);
24064         
24065     },
24066     
24067     insertText : function(txt)
24068     {
24069         
24070         
24071         range = this.createRange();
24072         range.deleteContents();
24073                //alert(Sender.getAttribute('label'));
24074                
24075         range.insertNode(this.doc.createTextNode(txt));
24076     } ,
24077     
24078     // private
24079     relayBtnCmd : function(btn){
24080         this.relayCmd(btn.cmd);
24081     },
24082
24083     /**
24084      * Executes a Midas editor command on the editor document and performs necessary focus and
24085      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24086      * @param {String} cmd The Midas command
24087      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24088      */
24089     relayCmd : function(cmd, value){
24090         this.win.focus();
24091         this.execCmd(cmd, value);
24092         this.fireEvent('editorevent', this);
24093         //this.updateToolbar();
24094         this.deferFocus();
24095     },
24096
24097     /**
24098      * Executes a Midas editor command directly on the editor document.
24099      * For visual commands, you should use {@link #relayCmd} instead.
24100      * <b>This should only be called after the editor is initialized.</b>
24101      * @param {String} cmd The Midas command
24102      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24103      */
24104     execCmd : function(cmd, value){
24105         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24106         this.syncValue();
24107     },
24108
24109     // private
24110     applyCommand : function(e){
24111         if(e.ctrlKey){
24112             var c = e.getCharCode(), cmd;
24113             if(c > 0){
24114                 c = String.fromCharCode(c);
24115                 switch(c){
24116                     case 'b':
24117                         cmd = 'bold';
24118                     break;
24119                     case 'i':
24120                         cmd = 'italic';
24121                     break;
24122                     case 'u':
24123                         cmd = 'underline';
24124                     break;
24125                 }
24126                 if(cmd){
24127                     this.win.focus();
24128                     this.execCmd(cmd);
24129                     this.deferFocus();
24130                     e.preventDefault();
24131                 }
24132             }
24133         }
24134     },
24135
24136     /**
24137      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24138      * to insert tRoo.
24139      * @param {String} text
24140      */
24141     insertAtCursor : function(text){
24142         if(!this.activated){
24143             return;
24144         }
24145         if(Roo.isIE){
24146             this.win.focus();
24147             var r = this.doc.selection.createRange();
24148             if(r){
24149                 r.collapse(true);
24150                 r.pasteHTML(text);
24151                 this.syncValue();
24152                 this.deferFocus();
24153             }
24154         }else if(Roo.isGecko || Roo.isOpera){
24155             this.win.focus();
24156             this.execCmd('InsertHTML', text);
24157             this.deferFocus();
24158         }else if(Roo.isSafari){
24159             this.execCmd('InsertText', text);
24160             this.deferFocus();
24161         }
24162     },
24163
24164     // private
24165     fixKeys : function(){ // load time branching for fastest keydown performance
24166         if(Roo.isIE){
24167             return function(e){
24168                 var k = e.getKey(), r;
24169                 if(k == e.TAB){
24170                     e.stopEvent();
24171                     r = this.doc.selection.createRange();
24172                     if(r){
24173                         r.collapse(true);
24174                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24175                         this.deferFocus();
24176                     }
24177                 }else if(k == e.ENTER){
24178                     r = this.doc.selection.createRange();
24179                     if(r){
24180                         var target = r.parentElement();
24181                         if(!target || target.tagName.toLowerCase() != 'li'){
24182                             e.stopEvent();
24183                             r.pasteHTML('<br />');
24184                             r.collapse(false);
24185                             r.select();
24186                         }
24187                     }
24188                 }
24189             };
24190         }else if(Roo.isOpera){
24191             return function(e){
24192                 var k = e.getKey();
24193                 if(k == e.TAB){
24194                     e.stopEvent();
24195                     this.win.focus();
24196                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24197                     this.deferFocus();
24198                 }
24199             };
24200         }else if(Roo.isSafari){
24201             return function(e){
24202                 var k = e.getKey();
24203                 if(k == e.TAB){
24204                     e.stopEvent();
24205                     this.execCmd('InsertText','\t');
24206                     this.deferFocus();
24207                 }
24208              };
24209         }
24210     }(),
24211     
24212     getAllAncestors: function()
24213     {
24214         var p = this.getSelectedNode();
24215         var a = [];
24216         if (!p) {
24217             a.push(p); // push blank onto stack..
24218             p = this.getParentElement();
24219         }
24220         
24221         
24222         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24223             a.push(p);
24224             p = p.parentNode;
24225         }
24226         a.push(this.doc.body);
24227         return a;
24228     },
24229     lastSel : false,
24230     lastSelNode : false,
24231     
24232     
24233     getSelection : function() 
24234     {
24235         this.assignDocWin();
24236         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24237     },
24238     
24239     getSelectedNode: function() 
24240     {
24241         // this may only work on Gecko!!!
24242         
24243         // should we cache this!!!!
24244         
24245         
24246         
24247          
24248         var range = this.createRange(this.getSelection());
24249         
24250         if (Roo.isIE) {
24251             var parent = range.parentElement();
24252             while (true) {
24253                 var testRange = range.duplicate();
24254                 testRange.moveToElementText(parent);
24255                 if (testRange.inRange(range)) {
24256                     break;
24257                 }
24258                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24259                     break;
24260                 }
24261                 parent = parent.parentElement;
24262             }
24263             return parent;
24264         }
24265         
24266         
24267         var ar = range.endContainer.childNodes;
24268         if (!ar.length) {
24269             ar = range.commonAncestorContainer.childNodes;
24270             //alert(ar.length);
24271         }
24272         var nodes = [];
24273         var other_nodes = [];
24274         var has_other_nodes = false;
24275         for (var i=0;i<ar.length;i++) {
24276             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24277                 continue;
24278             }
24279             // fullly contained node.
24280             
24281             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24282                 nodes.push(ar[i]);
24283                 continue;
24284             }
24285             
24286             // probably selected..
24287             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24288                 other_nodes.push(ar[i]);
24289                 continue;
24290             }
24291             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24292                 continue;
24293             }
24294             
24295             
24296             has_other_nodes = true;
24297         }
24298         if (!nodes.length && other_nodes.length) {
24299             nodes= other_nodes;
24300         }
24301         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24302             return false;
24303         }
24304         
24305         return nodes[0];
24306     },
24307     createRange: function(sel)
24308     {
24309         // this has strange effects when using with 
24310         // top toolbar - not sure if it's a great idea.
24311         //this.editor.contentWindow.focus();
24312         if (typeof sel != "undefined") {
24313             try {
24314                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24315             } catch(e) {
24316                 return this.doc.createRange();
24317             }
24318         } else {
24319             return this.doc.createRange();
24320         }
24321     },
24322     getParentElement: function()
24323     {
24324         
24325         this.assignDocWin();
24326         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24327         
24328         var range = this.createRange(sel);
24329          
24330         try {
24331             var p = range.commonAncestorContainer;
24332             while (p.nodeType == 3) { // text node
24333                 p = p.parentNode;
24334             }
24335             return p;
24336         } catch (e) {
24337             return null;
24338         }
24339     
24340     },
24341     
24342     
24343     
24344     // BC Hacks - cause I cant work out what i was trying to do..
24345     rangeIntersectsNode : function(range, node)
24346     {
24347         var nodeRange = node.ownerDocument.createRange();
24348         try {
24349             nodeRange.selectNode(node);
24350         }
24351         catch (e) {
24352             nodeRange.selectNodeContents(node);
24353         }
24354
24355         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24356                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24357     },
24358     rangeCompareNode : function(range, node) {
24359         var nodeRange = node.ownerDocument.createRange();
24360         try {
24361             nodeRange.selectNode(node);
24362         } catch (e) {
24363             nodeRange.selectNodeContents(node);
24364         }
24365         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24366         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24367
24368         if (nodeIsBefore && !nodeIsAfter)
24369             return 0;
24370         if (!nodeIsBefore && nodeIsAfter)
24371             return 1;
24372         if (nodeIsBefore && nodeIsAfter)
24373             return 2;
24374
24375         return 3;
24376     }
24377
24378     
24379     
24380     // hide stuff that is not compatible
24381     /**
24382      * @event blur
24383      * @hide
24384      */
24385     /**
24386      * @event change
24387      * @hide
24388      */
24389     /**
24390      * @event focus
24391      * @hide
24392      */
24393     /**
24394      * @event specialkey
24395      * @hide
24396      */
24397     /**
24398      * @cfg {String} fieldClass @hide
24399      */
24400     /**
24401      * @cfg {String} focusClass @hide
24402      */
24403     /**
24404      * @cfg {String} autoCreate @hide
24405      */
24406     /**
24407      * @cfg {String} inputType @hide
24408      */
24409     /**
24410      * @cfg {String} invalidClass @hide
24411      */
24412     /**
24413      * @cfg {String} invalidText @hide
24414      */
24415     /**
24416      * @cfg {String} msgFx @hide
24417      */
24418     /**
24419      * @cfg {String} validateOnBlur @hide
24420      */
24421 });// <script type="text/javascript">
24422 /*
24423  * Based on
24424  * Ext JS Library 1.1.1
24425  * Copyright(c) 2006-2007, Ext JS, LLC.
24426  *  
24427  
24428  */
24429
24430 /**
24431  * @class Roo.form.HtmlEditorToolbar1
24432  * Basic Toolbar
24433  * 
24434  * Usage:
24435  *
24436  new Roo.form.HtmlEditor({
24437     ....
24438     toolbars : [
24439         new Roo.form.HtmlEditorToolbar1({
24440             disable : { fonts: 1 , format: 1, ..., ... , ...],
24441             btns : [ .... ]
24442         })
24443     }
24444      
24445  * 
24446  * @cfg {Object} disable List of elements to disable..
24447  * @cfg {Array} btns List of additional buttons.
24448  * 
24449  * 
24450  * NEEDS Extra CSS? 
24451  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24452  */
24453  
24454 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24455 {
24456     
24457     Roo.apply(this, config);
24458     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24459     // dont call parent... till later.
24460 }
24461
24462 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24463     
24464     tb: false,
24465     
24466     rendered: false,
24467     
24468     editor : false,
24469     /**
24470      * @cfg {Object} disable  List of toolbar elements to disable
24471          
24472      */
24473     disable : false,
24474       /**
24475      * @cfg {Array} fontFamilies An array of available font families
24476      */
24477     fontFamilies : [
24478         'Arial',
24479         'Courier New',
24480         'Tahoma',
24481         'Times New Roman',
24482         'Verdana'
24483     ],
24484     
24485     specialChars : [
24486            "&#169;",
24487           "&#174;",     
24488           "&#8482;",    
24489           "&#163;" ,    
24490          // "&#8212;",    
24491           "&#8230;",    
24492           "&#247;" ,    
24493         //  "&#225;" ,     ?? a acute?
24494            "&#8364;"    , //Euro
24495        //   "&#8220;"    ,
24496         //  "&#8221;"    ,
24497         //  "&#8226;"    ,
24498           "&#176;"  //   , // degrees
24499
24500          // "&#233;"     , // e ecute
24501          // "&#250;"     , // u ecute?
24502     ],
24503     inputElements : [ 
24504             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24505             "input:submit", "input:button", "select", "textarea", "label" ],
24506     formats : [
24507         ["p"] ,  
24508         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24509         ["pre"],[ "code"], 
24510         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24511     ],
24512      /**
24513      * @cfg {String} defaultFont default font to use.
24514      */
24515     defaultFont: 'tahoma',
24516    
24517     fontSelect : false,
24518     
24519     
24520     formatCombo : false,
24521     
24522     init : function(editor)
24523     {
24524         this.editor = editor;
24525         
24526         
24527         var fid = editor.frameId;
24528         var etb = this;
24529         function btn(id, toggle, handler){
24530             var xid = fid + '-'+ id ;
24531             return {
24532                 id : xid,
24533                 cmd : id,
24534                 cls : 'x-btn-icon x-edit-'+id,
24535                 enableToggle:toggle !== false,
24536                 scope: editor, // was editor...
24537                 handler:handler||editor.relayBtnCmd,
24538                 clickEvent:'mousedown',
24539                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24540                 tabIndex:-1
24541             };
24542         }
24543         
24544         
24545         
24546         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24547         this.tb = tb;
24548          // stop form submits
24549         tb.el.on('click', function(e){
24550             e.preventDefault(); // what does this do?
24551         });
24552
24553         if(!this.disable.font && !Roo.isSafari){
24554             /* why no safari for fonts
24555             editor.fontSelect = tb.el.createChild({
24556                 tag:'select',
24557                 tabIndex: -1,
24558                 cls:'x-font-select',
24559                 html: editor.createFontOptions()
24560             });
24561             editor.fontSelect.on('change', function(){
24562                 var font = editor.fontSelect.dom.value;
24563                 editor.relayCmd('fontname', font);
24564                 editor.deferFocus();
24565             }, editor);
24566             tb.add(
24567                 editor.fontSelect.dom,
24568                 '-'
24569             );
24570             */
24571         };
24572         if(!this.disable.formats){
24573             this.formatCombo = new Roo.form.ComboBox({
24574                 store: new Roo.data.SimpleStore({
24575                     id : 'tag',
24576                     fields: ['tag'],
24577                     data : this.formats // from states.js
24578                 }),
24579                 blockFocus : true,
24580                 //autoCreate : {tag: "div",  size: "20"},
24581                 displayField:'tag',
24582                 typeAhead: false,
24583                 mode: 'local',
24584                 editable : false,
24585                 triggerAction: 'all',
24586                 emptyText:'Add tag',
24587                 selectOnFocus:true,
24588                 width:135,
24589                 listeners : {
24590                     'select': function(c, r, i) {
24591                         editor.insertTag(r.get('tag'));
24592                         editor.focus();
24593                     }
24594                 }
24595
24596             });
24597             tb.addField(this.formatCombo);
24598             
24599         }
24600         
24601         if(!this.disable.format){
24602             tb.add(
24603                 btn('bold'),
24604                 btn('italic'),
24605                 btn('underline')
24606             );
24607         };
24608         if(!this.disable.fontSize){
24609             tb.add(
24610                 '-',
24611                 
24612                 
24613                 btn('increasefontsize', false, editor.adjustFont),
24614                 btn('decreasefontsize', false, editor.adjustFont)
24615             );
24616         };
24617         
24618         
24619         if(this.disable.colors){
24620             tb.add(
24621                 '-', {
24622                     id:editor.frameId +'-forecolor',
24623                     cls:'x-btn-icon x-edit-forecolor',
24624                     clickEvent:'mousedown',
24625                     tooltip: this.buttonTips['forecolor'] || undefined,
24626                     tabIndex:-1,
24627                     menu : new Roo.menu.ColorMenu({
24628                         allowReselect: true,
24629                         focus: Roo.emptyFn,
24630                         value:'000000',
24631                         plain:true,
24632                         selectHandler: function(cp, color){
24633                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24634                             editor.deferFocus();
24635                         },
24636                         scope: editor,
24637                         clickEvent:'mousedown'
24638                     })
24639                 }, {
24640                     id:editor.frameId +'backcolor',
24641                     cls:'x-btn-icon x-edit-backcolor',
24642                     clickEvent:'mousedown',
24643                     tooltip: this.buttonTips['backcolor'] || undefined,
24644                     tabIndex:-1,
24645                     menu : new Roo.menu.ColorMenu({
24646                         focus: Roo.emptyFn,
24647                         value:'FFFFFF',
24648                         plain:true,
24649                         allowReselect: true,
24650                         selectHandler: function(cp, color){
24651                             if(Roo.isGecko){
24652                                 editor.execCmd('useCSS', false);
24653                                 editor.execCmd('hilitecolor', color);
24654                                 editor.execCmd('useCSS', true);
24655                                 editor.deferFocus();
24656                             }else{
24657                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
24658                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
24659                                 editor.deferFocus();
24660                             }
24661                         },
24662                         scope:editor,
24663                         clickEvent:'mousedown'
24664                     })
24665                 }
24666             );
24667         };
24668         // now add all the items...
24669         
24670
24671         if(!this.disable.alignments){
24672             tb.add(
24673                 '-',
24674                 btn('justifyleft'),
24675                 btn('justifycenter'),
24676                 btn('justifyright')
24677             );
24678         };
24679
24680         //if(!Roo.isSafari){
24681             if(!this.disable.links){
24682                 tb.add(
24683                     '-',
24684                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
24685                 );
24686             };
24687
24688             if(!this.disable.lists){
24689                 tb.add(
24690                     '-',
24691                     btn('insertorderedlist'),
24692                     btn('insertunorderedlist')
24693                 );
24694             }
24695             if(!this.disable.sourceEdit){
24696                 tb.add(
24697                     '-',
24698                     btn('sourceedit', true, function(btn){
24699                         this.toggleSourceEdit(btn.pressed);
24700                     })
24701                 );
24702             }
24703         //}
24704         
24705         var smenu = { };
24706         // special menu.. - needs to be tidied up..
24707         if (!this.disable.special) {
24708             smenu = {
24709                 text: "&#169;",
24710                 cls: 'x-edit-none',
24711                 menu : {
24712                     items : []
24713                    }
24714             };
24715             for (var i =0; i < this.specialChars.length; i++) {
24716                 smenu.menu.items.push({
24717                     
24718                     text: this.specialChars[i],
24719                     handler: function(a,b) {
24720                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
24721                     },
24722                     tabIndex:-1
24723                 });
24724             }
24725             
24726             
24727             tb.add(smenu);
24728             
24729             
24730         }
24731         if (this.btns) {
24732             for(var i =0; i< this.btns.length;i++) {
24733                 var b = this.btns[i];
24734                 b.cls =  'x-edit-none';
24735                 b.scope = editor;
24736                 tb.add(b);
24737             }
24738         
24739         }
24740         
24741         
24742         
24743         // disable everything...
24744         
24745         this.tb.items.each(function(item){
24746            if(item.id != editor.frameId+ '-sourceedit'){
24747                 item.disable();
24748             }
24749         });
24750         this.rendered = true;
24751         
24752         // the all the btns;
24753         editor.on('editorevent', this.updateToolbar, this);
24754         // other toolbars need to implement this..
24755         //editor.on('editmodechange', this.updateToolbar, this);
24756     },
24757     
24758     
24759     
24760     /**
24761      * Protected method that will not generally be called directly. It triggers
24762      * a toolbar update by reading the markup state of the current selection in the editor.
24763      */
24764     updateToolbar: function(){
24765
24766         if(!this.editor.activated){
24767             this.editor.onFirstFocus();
24768             return;
24769         }
24770
24771         var btns = this.tb.items.map, 
24772             doc = this.editor.doc,
24773             frameId = this.editor.frameId;
24774
24775         if(!this.disable.font && !Roo.isSafari){
24776             /*
24777             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
24778             if(name != this.fontSelect.dom.value){
24779                 this.fontSelect.dom.value = name;
24780             }
24781             */
24782         }
24783         if(!this.disable.format){
24784             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
24785             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
24786             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
24787         }
24788         if(!this.disable.alignments){
24789             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
24790             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
24791             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
24792         }
24793         if(!Roo.isSafari && !this.disable.lists){
24794             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
24795             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
24796         }
24797         
24798         var ans = this.editor.getAllAncestors();
24799         if (this.formatCombo) {
24800             
24801             
24802             var store = this.formatCombo.store;
24803             this.formatCombo.setValue("");
24804             for (var i =0; i < ans.length;i++) {
24805                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
24806                     // select it..
24807                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24808                     break;
24809                 }
24810             }
24811         }
24812         
24813         
24814         
24815         // hides menus... - so this cant be on a menu...
24816         Roo.menu.MenuMgr.hideAll();
24817
24818         //this.editorsyncValue();
24819     },
24820    
24821     
24822     createFontOptions : function(){
24823         var buf = [], fs = this.fontFamilies, ff, lc;
24824         for(var i = 0, len = fs.length; i< len; i++){
24825             ff = fs[i];
24826             lc = ff.toLowerCase();
24827             buf.push(
24828                 '<option value="',lc,'" style="font-family:',ff,';"',
24829                     (this.defaultFont == lc ? ' selected="true">' : '>'),
24830                     ff,
24831                 '</option>'
24832             );
24833         }
24834         return buf.join('');
24835     },
24836     
24837     toggleSourceEdit : function(sourceEditMode){
24838         if(sourceEditMode === undefined){
24839             sourceEditMode = !this.sourceEditMode;
24840         }
24841         this.sourceEditMode = sourceEditMode === true;
24842         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
24843         // just toggle the button?
24844         if(btn.pressed !== this.editor.sourceEditMode){
24845             btn.toggle(this.editor.sourceEditMode);
24846             return;
24847         }
24848         
24849         if(this.sourceEditMode){
24850             this.tb.items.each(function(item){
24851                 if(item.cmd != 'sourceedit'){
24852                     item.disable();
24853                 }
24854             });
24855           
24856         }else{
24857             if(this.initialized){
24858                 this.tb.items.each(function(item){
24859                     item.enable();
24860                 });
24861             }
24862             
24863         }
24864         // tell the editor that it's been pressed..
24865         this.editor.toggleSourceEdit(sourceEditMode);
24866        
24867     },
24868      /**
24869      * Object collection of toolbar tooltips for the buttons in the editor. The key
24870      * is the command id associated with that button and the value is a valid QuickTips object.
24871      * For example:
24872 <pre><code>
24873 {
24874     bold : {
24875         title: 'Bold (Ctrl+B)',
24876         text: 'Make the selected text bold.',
24877         cls: 'x-html-editor-tip'
24878     },
24879     italic : {
24880         title: 'Italic (Ctrl+I)',
24881         text: 'Make the selected text italic.',
24882         cls: 'x-html-editor-tip'
24883     },
24884     ...
24885 </code></pre>
24886     * @type Object
24887      */
24888     buttonTips : {
24889         bold : {
24890             title: 'Bold (Ctrl+B)',
24891             text: 'Make the selected text bold.',
24892             cls: 'x-html-editor-tip'
24893         },
24894         italic : {
24895             title: 'Italic (Ctrl+I)',
24896             text: 'Make the selected text italic.',
24897             cls: 'x-html-editor-tip'
24898         },
24899         underline : {
24900             title: 'Underline (Ctrl+U)',
24901             text: 'Underline the selected text.',
24902             cls: 'x-html-editor-tip'
24903         },
24904         increasefontsize : {
24905             title: 'Grow Text',
24906             text: 'Increase the font size.',
24907             cls: 'x-html-editor-tip'
24908         },
24909         decreasefontsize : {
24910             title: 'Shrink Text',
24911             text: 'Decrease the font size.',
24912             cls: 'x-html-editor-tip'
24913         },
24914         backcolor : {
24915             title: 'Text Highlight Color',
24916             text: 'Change the background color of the selected text.',
24917             cls: 'x-html-editor-tip'
24918         },
24919         forecolor : {
24920             title: 'Font Color',
24921             text: 'Change the color of the selected text.',
24922             cls: 'x-html-editor-tip'
24923         },
24924         justifyleft : {
24925             title: 'Align Text Left',
24926             text: 'Align text to the left.',
24927             cls: 'x-html-editor-tip'
24928         },
24929         justifycenter : {
24930             title: 'Center Text',
24931             text: 'Center text in the editor.',
24932             cls: 'x-html-editor-tip'
24933         },
24934         justifyright : {
24935             title: 'Align Text Right',
24936             text: 'Align text to the right.',
24937             cls: 'x-html-editor-tip'
24938         },
24939         insertunorderedlist : {
24940             title: 'Bullet List',
24941             text: 'Start a bulleted list.',
24942             cls: 'x-html-editor-tip'
24943         },
24944         insertorderedlist : {
24945             title: 'Numbered List',
24946             text: 'Start a numbered list.',
24947             cls: 'x-html-editor-tip'
24948         },
24949         createlink : {
24950             title: 'Hyperlink',
24951             text: 'Make the selected text a hyperlink.',
24952             cls: 'x-html-editor-tip'
24953         },
24954         sourceedit : {
24955             title: 'Source Edit',
24956             text: 'Switch to source editing mode.',
24957             cls: 'x-html-editor-tip'
24958         }
24959     },
24960     // private
24961     onDestroy : function(){
24962         if(this.rendered){
24963             
24964             this.tb.items.each(function(item){
24965                 if(item.menu){
24966                     item.menu.removeAll();
24967                     if(item.menu.el){
24968                         item.menu.el.destroy();
24969                     }
24970                 }
24971                 item.destroy();
24972             });
24973              
24974         }
24975     },
24976     onFirstFocus: function() {
24977         this.tb.items.each(function(item){
24978            item.enable();
24979         });
24980     }
24981 });
24982
24983
24984
24985
24986 // <script type="text/javascript">
24987 /*
24988  * Based on
24989  * Ext JS Library 1.1.1
24990  * Copyright(c) 2006-2007, Ext JS, LLC.
24991  *  
24992  
24993  */
24994
24995  
24996 /**
24997  * @class Roo.form.HtmlEditor.ToolbarContext
24998  * Context Toolbar
24999  * 
25000  * Usage:
25001  *
25002  new Roo.form.HtmlEditor({
25003     ....
25004     toolbars : [
25005         new Roo.form.HtmlEditor.ToolbarStandard(),
25006         new Roo.form.HtmlEditor.ToolbarContext()
25007         })
25008     }
25009      
25010  * 
25011  * @config : {Object} disable List of elements to disable.. (not done yet.)
25012  * 
25013  * 
25014  */
25015
25016 Roo.form.HtmlEditor.ToolbarContext = function(config)
25017 {
25018     
25019     Roo.apply(this, config);
25020     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25021     // dont call parent... till later.
25022 }
25023 Roo.form.HtmlEditor.ToolbarContext.types = {
25024     'IMG' : {
25025         width : {
25026             title: "Width",
25027             width: 40
25028         },
25029         height:  {
25030             title: "Height",
25031             width: 40
25032         },
25033         align: {
25034             title: "Align",
25035             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25036             width : 80
25037             
25038         },
25039         border: {
25040             title: "Border",
25041             width: 40
25042         },
25043         alt: {
25044             title: "Alt",
25045             width: 120
25046         },
25047         src : {
25048             title: "Src",
25049             width: 220
25050         }
25051         
25052     },
25053     'A' : {
25054         name : {
25055             title: "Name",
25056             width: 50
25057         },
25058         href:  {
25059             title: "Href",
25060             width: 220
25061         } // border?
25062         
25063     },
25064     'TABLE' : {
25065         rows : {
25066             title: "Rows",
25067             width: 20
25068         },
25069         cols : {
25070             title: "Cols",
25071             width: 20
25072         },
25073         width : {
25074             title: "Width",
25075             width: 40
25076         },
25077         height : {
25078             title: "Height",
25079             width: 40
25080         },
25081         border : {
25082             title: "Border",
25083             width: 20
25084         }
25085     },
25086     'TD' : {
25087         width : {
25088             title: "Width",
25089             width: 40
25090         },
25091         height : {
25092             title: "Height",
25093             width: 40
25094         },   
25095         align: {
25096             title: "Align",
25097             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25098             width: 40
25099         },
25100         valign: {
25101             title: "Valign",
25102             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25103             width: 40
25104         },
25105         colspan: {
25106             title: "Colspan",
25107             width: 20
25108             
25109         }
25110     },
25111     'INPUT' : {
25112         name : {
25113             title: "name",
25114             width: 120
25115         },
25116         value : {
25117             title: "Value",
25118             width: 120
25119         },
25120         width : {
25121             title: "Width",
25122             width: 40
25123         }
25124     },
25125     'LABEL' : {
25126         'for' : {
25127             title: "For",
25128             width: 120
25129         }
25130     },
25131     'TEXTAREA' : {
25132           name : {
25133             title: "name",
25134             width: 120
25135         },
25136         rows : {
25137             title: "Rows",
25138             width: 20
25139         },
25140         cols : {
25141             title: "Cols",
25142             width: 20
25143         }
25144     },
25145     'SELECT' : {
25146         name : {
25147             title: "name",
25148             width: 120
25149         },
25150         selectoptions : {
25151             title: "Options",
25152             width: 200
25153         }
25154     },
25155     'BODY' : {
25156         title : {
25157             title: "title",
25158             width: 120,
25159             disabled : true
25160         }
25161     }
25162 };
25163
25164
25165
25166 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25167     
25168     tb: false,
25169     
25170     rendered: false,
25171     
25172     editor : false,
25173     /**
25174      * @cfg {Object} disable  List of toolbar elements to disable
25175          
25176      */
25177     disable : false,
25178     
25179     
25180     
25181     toolbars : false,
25182     
25183     init : function(editor)
25184     {
25185         this.editor = editor;
25186         
25187         
25188         var fid = editor.frameId;
25189         var etb = this;
25190         function btn(id, toggle, handler){
25191             var xid = fid + '-'+ id ;
25192             return {
25193                 id : xid,
25194                 cmd : id,
25195                 cls : 'x-btn-icon x-edit-'+id,
25196                 enableToggle:toggle !== false,
25197                 scope: editor, // was editor...
25198                 handler:handler||editor.relayBtnCmd,
25199                 clickEvent:'mousedown',
25200                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25201                 tabIndex:-1
25202             };
25203         }
25204         // create a new element.
25205         var wdiv = editor.wrap.createChild({
25206                 tag: 'div'
25207             }, editor.wrap.dom.firstChild.nextSibling, true);
25208         
25209         // can we do this more than once??
25210         
25211          // stop form submits
25212       
25213  
25214         // disable everything...
25215         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25216         this.toolbars = {};
25217            
25218         for (var i in  ty) {
25219             this.toolbars[i] = this.buildToolbar(ty[i],i);
25220         }
25221         this.tb = this.toolbars.BODY;
25222         this.tb.el.show();
25223         
25224          
25225         this.rendered = true;
25226         
25227         // the all the btns;
25228         editor.on('editorevent', this.updateToolbar, this);
25229         // other toolbars need to implement this..
25230         //editor.on('editmodechange', this.updateToolbar, this);
25231     },
25232     
25233     
25234     
25235     /**
25236      * Protected method that will not generally be called directly. It triggers
25237      * a toolbar update by reading the markup state of the current selection in the editor.
25238      */
25239     updateToolbar: function(){
25240
25241         if(!this.editor.activated){
25242             this.editor.onFirstFocus();
25243             return;
25244         }
25245
25246         
25247         var ans = this.editor.getAllAncestors();
25248         
25249         // pick
25250         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25251         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25252         sel = sel ? sel : this.editor.doc.body;
25253         sel = sel.tagName.length ? sel : this.editor.doc.body;
25254         var tn = sel.tagName.toUpperCase();
25255         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25256         tn = sel.tagName.toUpperCase();
25257         if (this.tb.name  == tn) {
25258             return; // no change
25259         }
25260         this.tb.el.hide();
25261         ///console.log("show: " + tn);
25262         this.tb =  this.toolbars[tn];
25263         this.tb.el.show();
25264         this.tb.fields.each(function(e) {
25265             e.setValue(sel.getAttribute(e.name));
25266         });
25267         this.tb.selectedNode = sel;
25268         
25269         
25270         Roo.menu.MenuMgr.hideAll();
25271
25272         //this.editorsyncValue();
25273     },
25274    
25275        
25276     // private
25277     onDestroy : function(){
25278         if(this.rendered){
25279             
25280             this.tb.items.each(function(item){
25281                 if(item.menu){
25282                     item.menu.removeAll();
25283                     if(item.menu.el){
25284                         item.menu.el.destroy();
25285                     }
25286                 }
25287                 item.destroy();
25288             });
25289              
25290         }
25291     },
25292     onFirstFocus: function() {
25293         // need to do this for all the toolbars..
25294         this.tb.items.each(function(item){
25295            item.enable();
25296         });
25297     },
25298     buildToolbar: function(tlist, nm)
25299     {
25300         var editor = this.editor;
25301          // create a new element.
25302         var wdiv = editor.wrap.createChild({
25303                 tag: 'div'
25304             }, editor.wrap.dom.firstChild.nextSibling, true);
25305         
25306        
25307         var tb = new Roo.Toolbar(wdiv);
25308         tb.add(nm+ ":&nbsp;");
25309         for (var i in tlist) {
25310             var item = tlist[i];
25311             tb.add(item.title + ":&nbsp;");
25312             if (item.opts) {
25313                 // fixme
25314                 
25315               
25316                 tb.addField( new Roo.form.ComboBox({
25317                     store: new Roo.data.SimpleStore({
25318                         id : 'val',
25319                         fields: ['val'],
25320                         data : item.opts // from states.js
25321                     }),
25322                     name : i,
25323                     displayField:'val',
25324                     typeAhead: false,
25325                     mode: 'local',
25326                     editable : false,
25327                     triggerAction: 'all',
25328                     emptyText:'Select',
25329                     selectOnFocus:true,
25330                     width: item.width ? item.width  : 130,
25331                     listeners : {
25332                         'select': function(c, r, i) {
25333                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25334                         }
25335                     }
25336
25337                 }));
25338                 continue;
25339                     
25340                 
25341                 
25342                 
25343                 
25344                 tb.addField( new Roo.form.TextField({
25345                     name: i,
25346                     width: 100,
25347                     //allowBlank:false,
25348                     value: ''
25349                 }));
25350                 continue;
25351             }
25352             tb.addField( new Roo.form.TextField({
25353                 name: i,
25354                 width: item.width,
25355                 //allowBlank:true,
25356                 value: '',
25357                 listeners: {
25358                     'change' : function(f, nv, ov) {
25359                         tb.selectedNode.setAttribute(f.name, nv);
25360                     }
25361                 }
25362             }));
25363              
25364         }
25365         tb.el.on('click', function(e){
25366             e.preventDefault(); // what does this do?
25367         });
25368         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25369         tb.el.hide();
25370         tb.name = nm;
25371         // dont need to disable them... as they will get hidden
25372         return tb;
25373          
25374         
25375     }
25376     
25377     
25378     
25379     
25380 });
25381
25382
25383
25384
25385
25386 /*
25387  * Based on:
25388  * Ext JS Library 1.1.1
25389  * Copyright(c) 2006-2007, Ext JS, LLC.
25390  *
25391  * Originally Released Under LGPL - original licence link has changed is not relivant.
25392  *
25393  * Fork - LGPL
25394  * <script type="text/javascript">
25395  */
25396  
25397 /**
25398  * @class Roo.form.BasicForm
25399  * @extends Roo.util.Observable
25400  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25401  * @constructor
25402  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25403  * @param {Object} config Configuration options
25404  */
25405 Roo.form.BasicForm = function(el, config){
25406     this.allItems = [];
25407     this.childForms = [];
25408     Roo.apply(this, config);
25409     /*
25410      * The Roo.form.Field items in this form.
25411      * @type MixedCollection
25412      */
25413      
25414      
25415     this.items = new Roo.util.MixedCollection(false, function(o){
25416         return o.id || (o.id = Roo.id());
25417     });
25418     this.addEvents({
25419         /**
25420          * @event beforeaction
25421          * Fires before any action is performed. Return false to cancel the action.
25422          * @param {Form} this
25423          * @param {Action} action The action to be performed
25424          */
25425         beforeaction: true,
25426         /**
25427          * @event actionfailed
25428          * Fires when an action fails.
25429          * @param {Form} this
25430          * @param {Action} action The action that failed
25431          */
25432         actionfailed : true,
25433         /**
25434          * @event actioncomplete
25435          * Fires when an action is completed.
25436          * @param {Form} this
25437          * @param {Action} action The action that completed
25438          */
25439         actioncomplete : true
25440     });
25441     if(el){
25442         this.initEl(el);
25443     }
25444     Roo.form.BasicForm.superclass.constructor.call(this);
25445 };
25446
25447 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25448     /**
25449      * @cfg {String} method
25450      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25451      */
25452     /**
25453      * @cfg {DataReader} reader
25454      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25455      * This is optional as there is built-in support for processing JSON.
25456      */
25457     /**
25458      * @cfg {DataReader} errorReader
25459      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25460      * This is completely optional as there is built-in support for processing JSON.
25461      */
25462     /**
25463      * @cfg {String} url
25464      * The URL to use for form actions if one isn't supplied in the action options.
25465      */
25466     /**
25467      * @cfg {Boolean} fileUpload
25468      * Set to true if this form is a file upload.
25469      */
25470     /**
25471      * @cfg {Object} baseParams
25472      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25473      */
25474     /**
25475      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25476      */
25477     timeout: 30,
25478
25479     // private
25480     activeAction : null,
25481
25482     /**
25483      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25484      * or setValues() data instead of when the form was first created.
25485      */
25486     trackResetOnLoad : false,
25487     
25488     
25489     /**
25490      * childForms - used for multi-tab forms
25491      * @type {Array}
25492      */
25493     childForms : false,
25494     
25495     /**
25496      * allItems - full list of fields.
25497      * @type {Array}
25498      */
25499     allItems : false,
25500     
25501     /**
25502      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
25503      * element by passing it or its id or mask the form itself by passing in true.
25504      * @type Mixed
25505      */
25506     waitMsgTarget : undefined,
25507
25508     // private
25509     initEl : function(el){
25510         this.el = Roo.get(el);
25511         this.id = this.el.id || Roo.id();
25512         this.el.on('submit', this.onSubmit, this);
25513         this.el.addClass('x-form');
25514     },
25515
25516     // private
25517     onSubmit : function(e){
25518         e.stopEvent();
25519     },
25520
25521     /**
25522      * Returns true if client-side validation on the form is successful.
25523      * @return Boolean
25524      */
25525     isValid : function(){
25526         var valid = true;
25527         this.items.each(function(f){
25528            if(!f.validate()){
25529                valid = false;
25530            }
25531         });
25532         return valid;
25533     },
25534
25535     /**
25536      * Returns true if any fields in this form have changed since their original load.
25537      * @return Boolean
25538      */
25539     isDirty : function(){
25540         var dirty = false;
25541         this.items.each(function(f){
25542            if(f.isDirty()){
25543                dirty = true;
25544                return false;
25545            }
25546         });
25547         return dirty;
25548     },
25549
25550     /**
25551      * Performs a predefined action (submit or load) or custom actions you define on this form.
25552      * @param {String} actionName The name of the action type
25553      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25554      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25555      * accept other config options):
25556      * <pre>
25557 Property          Type             Description
25558 ----------------  ---------------  ----------------------------------------------------------------------------------
25559 url               String           The url for the action (defaults to the form's url)
25560 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25561 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25562 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25563                                    validate the form on the client (defaults to false)
25564      * </pre>
25565      * @return {BasicForm} this
25566      */
25567     doAction : function(action, options){
25568         if(typeof action == 'string'){
25569             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25570         }
25571         if(this.fireEvent('beforeaction', this, action) !== false){
25572             this.beforeAction(action);
25573             action.run.defer(100, action);
25574         }
25575         return this;
25576     },
25577
25578     /**
25579      * Shortcut to do a submit action.
25580      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25581      * @return {BasicForm} this
25582      */
25583     submit : function(options){
25584         this.doAction('submit', options);
25585         return this;
25586     },
25587
25588     /**
25589      * Shortcut to do a load action.
25590      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25591      * @return {BasicForm} this
25592      */
25593     load : function(options){
25594         this.doAction('load', options);
25595         return this;
25596     },
25597
25598     /**
25599      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25600      * @param {Record} record The record to edit
25601      * @return {BasicForm} this
25602      */
25603     updateRecord : function(record){
25604         record.beginEdit();
25605         var fs = record.fields;
25606         fs.each(function(f){
25607             var field = this.findField(f.name);
25608             if(field){
25609                 record.set(f.name, field.getValue());
25610             }
25611         }, this);
25612         record.endEdit();
25613         return this;
25614     },
25615
25616     /**
25617      * Loads an Roo.data.Record into this form.
25618      * @param {Record} record The record to load
25619      * @return {BasicForm} this
25620      */
25621     loadRecord : function(record){
25622         this.setValues(record.data);
25623         return this;
25624     },
25625
25626     // private
25627     beforeAction : function(action){
25628         var o = action.options;
25629         if(o.waitMsg){
25630             if(this.waitMsgTarget === true){
25631                 this.el.mask(o.waitMsg, 'x-mask-loading');
25632             }else if(this.waitMsgTarget){
25633                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25634                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
25635             }else{
25636                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
25637             }
25638         }
25639     },
25640
25641     // private
25642     afterAction : function(action, success){
25643         this.activeAction = null;
25644         var o = action.options;
25645         if(o.waitMsg){
25646             if(this.waitMsgTarget === true){
25647                 this.el.unmask();
25648             }else if(this.waitMsgTarget){
25649                 this.waitMsgTarget.unmask();
25650             }else{
25651                 Roo.MessageBox.updateProgress(1);
25652                 Roo.MessageBox.hide();
25653             }
25654         }
25655         if(success){
25656             if(o.reset){
25657                 this.reset();
25658             }
25659             Roo.callback(o.success, o.scope, [this, action]);
25660             this.fireEvent('actioncomplete', this, action);
25661         }else{
25662             Roo.callback(o.failure, o.scope, [this, action]);
25663             this.fireEvent('actionfailed', this, action);
25664         }
25665     },
25666
25667     /**
25668      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25669      * @param {String} id The value to search for
25670      * @return Field
25671      */
25672     findField : function(id){
25673         var field = this.items.get(id);
25674         if(!field){
25675             this.items.each(function(f){
25676                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25677                     field = f;
25678                     return false;
25679                 }
25680             });
25681         }
25682         return field || null;
25683     },
25684
25685     /**
25686      * Add a secondary form to this one, 
25687      * Used to provide tabbed forms. One form is primary, with hidden values 
25688      * which mirror the elements from the other forms.
25689      * 
25690      * @param {Roo.form.Form} form to add.
25691      * 
25692      */
25693     addForm : function(form)
25694     {
25695        
25696         if (this.childForms.indexOf(form) > -1) {
25697             // already added..
25698             return;
25699         }
25700         this.childForms.push(form);
25701         Roo.each(form.allItems, function (fe) {
25702             
25703             if (this.findField(fe.name)) { // already added..
25704                 return;
25705             }
25706             var add = new Roo.form.Hidden({
25707                 name : fe.name
25708             });
25709             add.render(this.el);
25710             
25711             this.add( add );
25712         }, this);
25713         
25714     },
25715     /**
25716      * Mark fields in this form invalid in bulk.
25717      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25718      * @return {BasicForm} this
25719      */
25720     markInvalid : function(errors){
25721         if(errors instanceof Array){
25722             for(var i = 0, len = errors.length; i < len; i++){
25723                 var fieldError = errors[i];
25724                 var f = this.findField(fieldError.id);
25725                 if(f){
25726                     f.markInvalid(fieldError.msg);
25727                 }
25728             }
25729         }else{
25730             var field, id;
25731             for(id in errors){
25732                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25733                     field.markInvalid(errors[id]);
25734                 }
25735             }
25736         }
25737         Roo.each(this.childForms || [], function (f) {
25738             f.markInvalid(errors);
25739         });
25740         
25741         return this;
25742     },
25743
25744     /**
25745      * Set values for fields in this form in bulk.
25746      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25747      * @return {BasicForm} this
25748      */
25749     setValues : function(values){
25750         if(values instanceof Array){ // array of objects
25751             for(var i = 0, len = values.length; i < len; i++){
25752                 var v = values[i];
25753                 var f = this.findField(v.id);
25754                 if(f){
25755                     f.setValue(v.value);
25756                     if(this.trackResetOnLoad){
25757                         f.originalValue = f.getValue();
25758                     }
25759                 }
25760             }
25761         }else{ // object hash
25762             var field, id;
25763             for(id in values){
25764                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25765                     
25766                     if (field.setFromData && 
25767                         field.valueField && 
25768                         field.displayField &&
25769                         // combos' with local stores can 
25770                         // be queried via setValue()
25771                         // to set their value..
25772                         (field.store && !field.store.isLocal)
25773                         ) {
25774                         // it's a combo
25775                         var sd = { };
25776                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25777                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25778                         field.setFromData(sd);
25779                         
25780                     } else {
25781                         field.setValue(values[id]);
25782                     }
25783                     
25784                     
25785                     if(this.trackResetOnLoad){
25786                         field.originalValue = field.getValue();
25787                     }
25788                 }
25789             }
25790         }
25791          
25792         Roo.each(this.childForms || [], function (f) {
25793             f.setValues(values);
25794         });
25795                 
25796         return this;
25797     },
25798
25799     /**
25800      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25801      * they are returned as an array.
25802      * @param {Boolean} asString
25803      * @return {Object}
25804      */
25805     getValues : function(asString){
25806         if (this.childForms) {
25807             // copy values from the child forms
25808             Roo.each(this.childForms, function (f) {
25809                 this.setValues(f.getValues());
25810             }, this);
25811         }
25812         
25813         
25814         
25815         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25816         if(asString === true){
25817             return fs;
25818         }
25819         return Roo.urlDecode(fs);
25820     },
25821
25822     /**
25823      * Clears all invalid messages in this form.
25824      * @return {BasicForm} this
25825      */
25826     clearInvalid : function(){
25827         this.items.each(function(f){
25828            f.clearInvalid();
25829         });
25830         
25831         Roo.each(this.childForms || [], function (f) {
25832             f.clearInvalid();
25833         });
25834         
25835         
25836         return this;
25837     },
25838
25839     /**
25840      * Resets this form.
25841      * @return {BasicForm} this
25842      */
25843     reset : function(){
25844         this.items.each(function(f){
25845             f.reset();
25846         });
25847         
25848         Roo.each(this.childForms || [], function (f) {
25849             f.reset();
25850         });
25851        
25852         
25853         return this;
25854     },
25855
25856     /**
25857      * Add Roo.form components to this form.
25858      * @param {Field} field1
25859      * @param {Field} field2 (optional)
25860      * @param {Field} etc (optional)
25861      * @return {BasicForm} this
25862      */
25863     add : function(){
25864         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25865         return this;
25866     },
25867
25868
25869     /**
25870      * Removes a field from the items collection (does NOT remove its markup).
25871      * @param {Field} field
25872      * @return {BasicForm} this
25873      */
25874     remove : function(field){
25875         this.items.remove(field);
25876         return this;
25877     },
25878
25879     /**
25880      * Looks at the fields in this form, checks them for an id attribute,
25881      * and calls applyTo on the existing dom element with that id.
25882      * @return {BasicForm} this
25883      */
25884     render : function(){
25885         this.items.each(function(f){
25886             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25887                 f.applyTo(f.id);
25888             }
25889         });
25890         return this;
25891     },
25892
25893     /**
25894      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25895      * @param {Object} values
25896      * @return {BasicForm} this
25897      */
25898     applyToFields : function(o){
25899         this.items.each(function(f){
25900            Roo.apply(f, o);
25901         });
25902         return this;
25903     },
25904
25905     /**
25906      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25907      * @param {Object} values
25908      * @return {BasicForm} this
25909      */
25910     applyIfToFields : function(o){
25911         this.items.each(function(f){
25912            Roo.applyIf(f, o);
25913         });
25914         return this;
25915     }
25916 });
25917
25918 // back compat
25919 Roo.BasicForm = Roo.form.BasicForm;/*
25920  * Based on:
25921  * Ext JS Library 1.1.1
25922  * Copyright(c) 2006-2007, Ext JS, LLC.
25923  *
25924  * Originally Released Under LGPL - original licence link has changed is not relivant.
25925  *
25926  * Fork - LGPL
25927  * <script type="text/javascript">
25928  */
25929
25930 /**
25931  * @class Roo.form.Form
25932  * @extends Roo.form.BasicForm
25933  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25934  * @constructor
25935  * @param {Object} config Configuration options
25936  */
25937 Roo.form.Form = function(config){
25938     var xitems =  [];
25939     if (config.items) {
25940         xitems = config.items;
25941         delete config.items;
25942     }
25943    
25944     
25945     Roo.form.Form.superclass.constructor.call(this, null, config);
25946     this.url = this.url || this.action;
25947     if(!this.root){
25948         this.root = new Roo.form.Layout(Roo.applyIf({
25949             id: Roo.id()
25950         }, config));
25951     }
25952     this.active = this.root;
25953     /**
25954      * Array of all the buttons that have been added to this form via {@link addButton}
25955      * @type Array
25956      */
25957     this.buttons = [];
25958     this.allItems = [];
25959     this.addEvents({
25960         /**
25961          * @event clientvalidation
25962          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25963          * @param {Form} this
25964          * @param {Boolean} valid true if the form has passed client-side validation
25965          */
25966         clientvalidation: true,
25967         /**
25968          * @event rendered
25969          * Fires when the form is rendered
25970          * @param {Roo.form.Form} form
25971          */
25972         rendered : true
25973     });
25974     
25975     Roo.each(xitems, this.addxtype, this);
25976     
25977     
25978     
25979 };
25980
25981 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25982     /**
25983      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25984      */
25985     /**
25986      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25987      */
25988     /**
25989      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25990      */
25991     buttonAlign:'center',
25992
25993     /**
25994      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25995      */
25996     minButtonWidth:75,
25997
25998     /**
25999      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26000      * This property cascades to child containers if not set.
26001      */
26002     labelAlign:'left',
26003
26004     /**
26005      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26006      * fires a looping event with that state. This is required to bind buttons to the valid
26007      * state using the config value formBind:true on the button.
26008      */
26009     monitorValid : false,
26010
26011     /**
26012      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26013      */
26014     monitorPoll : 200,
26015
26016   
26017     /**
26018      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26019      * fields are added and the column is closed. If no fields are passed the column remains open
26020      * until end() is called.
26021      * @param {Object} config The config to pass to the column
26022      * @param {Field} field1 (optional)
26023      * @param {Field} field2 (optional)
26024      * @param {Field} etc (optional)
26025      * @return Column The column container object
26026      */
26027     column : function(c){
26028         var col = new Roo.form.Column(c);
26029         this.start(col);
26030         if(arguments.length > 1){ // duplicate code required because of Opera
26031             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26032             this.end();
26033         }
26034         return col;
26035     },
26036
26037     /**
26038      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26039      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26040      * until end() is called.
26041      * @param {Object} config The config to pass to the fieldset
26042      * @param {Field} field1 (optional)
26043      * @param {Field} field2 (optional)
26044      * @param {Field} etc (optional)
26045      * @return FieldSet The fieldset container object
26046      */
26047     fieldset : function(c){
26048         var fs = new Roo.form.FieldSet(c);
26049         this.start(fs);
26050         if(arguments.length > 1){ // duplicate code required because of Opera
26051             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26052             this.end();
26053         }
26054         return fs;
26055     },
26056
26057     /**
26058      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26059      * fields are added and the container is closed. If no fields are passed the container remains open
26060      * until end() is called.
26061      * @param {Object} config The config to pass to the Layout
26062      * @param {Field} field1 (optional)
26063      * @param {Field} field2 (optional)
26064      * @param {Field} etc (optional)
26065      * @return Layout The container object
26066      */
26067     container : function(c){
26068         var l = new Roo.form.Layout(c);
26069         this.start(l);
26070         if(arguments.length > 1){ // duplicate code required because of Opera
26071             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26072             this.end();
26073         }
26074         return l;
26075     },
26076
26077     /**
26078      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26079      * @param {Object} container A Roo.form.Layout or subclass of Layout
26080      * @return {Form} this
26081      */
26082     start : function(c){
26083         // cascade label info
26084         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26085         this.active.stack.push(c);
26086         c.ownerCt = this.active;
26087         this.active = c;
26088         return this;
26089     },
26090
26091     /**
26092      * Closes the current open container
26093      * @return {Form} this
26094      */
26095     end : function(){
26096         if(this.active == this.root){
26097             return this;
26098         }
26099         this.active = this.active.ownerCt;
26100         return this;
26101     },
26102
26103     /**
26104      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26105      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26106      * as the label of the field.
26107      * @param {Field} field1
26108      * @param {Field} field2 (optional)
26109      * @param {Field} etc. (optional)
26110      * @return {Form} this
26111      */
26112     add : function(){
26113         this.active.stack.push.apply(this.active.stack, arguments);
26114         this.allItems.push.apply(this.allItems,arguments);
26115         var r = [];
26116         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26117             if(a[i].isFormField){
26118                 r.push(a[i]);
26119             }
26120         }
26121         if(r.length > 0){
26122             Roo.form.Form.superclass.add.apply(this, r);
26123         }
26124         return this;
26125     },
26126     
26127
26128     
26129     
26130     
26131      /**
26132      * Find any element that has been added to a form, using it's ID or name
26133      * This can include framesets, columns etc. along with regular fields..
26134      * @param {String} id - id or name to find.
26135      
26136      * @return {Element} e - or false if nothing found.
26137      */
26138     findbyId : function(id)
26139     {
26140         var ret = false;
26141         if (!id) {
26142             return ret;
26143         }
26144         Ext.each(this.allItems, function(f){
26145             if (f.id == id || f.name == id ){
26146                 ret = f;
26147                 return false;
26148             }
26149         });
26150         return ret;
26151     },
26152
26153     
26154     
26155     /**
26156      * Render this form into the passed container. This should only be called once!
26157      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26158      * @return {Form} this
26159      */
26160     render : function(ct){
26161         ct = Roo.get(ct);
26162         var o = this.autoCreate || {
26163             tag: 'form',
26164             method : this.method || 'POST',
26165             id : this.id || Roo.id()
26166         };
26167         this.initEl(ct.createChild(o));
26168
26169         this.root.render(this.el);
26170
26171         this.items.each(function(f){
26172             f.render('x-form-el-'+f.id);
26173         });
26174
26175         if(this.buttons.length > 0){
26176             // tables are required to maintain order and for correct IE layout
26177             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26178                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26179                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26180             }}, null, true);
26181             var tr = tb.getElementsByTagName('tr')[0];
26182             for(var i = 0, len = this.buttons.length; i < len; i++) {
26183                 var b = this.buttons[i];
26184                 var td = document.createElement('td');
26185                 td.className = 'x-form-btn-td';
26186                 b.render(tr.appendChild(td));
26187             }
26188         }
26189         if(this.monitorValid){ // initialize after render
26190             this.startMonitoring();
26191         }
26192         this.fireEvent('rendered', this);
26193         return this;
26194     },
26195
26196     /**
26197      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26198      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26199      * object or a valid Roo.DomHelper element config
26200      * @param {Function} handler The function called when the button is clicked
26201      * @param {Object} scope (optional) The scope of the handler function
26202      * @return {Roo.Button}
26203      */
26204     addButton : function(config, handler, scope){
26205         var bc = {
26206             handler: handler,
26207             scope: scope,
26208             minWidth: this.minButtonWidth,
26209             hideParent:true
26210         };
26211         if(typeof config == "string"){
26212             bc.text = config;
26213         }else{
26214             Roo.apply(bc, config);
26215         }
26216         var btn = new Roo.Button(null, bc);
26217         this.buttons.push(btn);
26218         return btn;
26219     },
26220
26221      /**
26222      * Adds a series of form elements (using the xtype property as the factory method.
26223      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26224      * @param {Object} config 
26225      */
26226     
26227     addxtype : function()
26228     {
26229         var ar = Array.prototype.slice.call(arguments, 0);
26230         var ret = false;
26231         for(var i = 0; i < ar.length; i++) {
26232             if (!ar[i]) {
26233                 continue; // skip -- if this happends something invalid got sent, we 
26234                 // should ignore it, as basically that interface element will not show up
26235                 // and that should be pretty obvious!!
26236             }
26237             
26238             if (Roo.form[ar[i].xtype]) {
26239                 ar[i].form = this;
26240                 var fe = Roo.factory(ar[i], Roo.form);
26241                 if (!ret) {
26242                     ret = fe;
26243                 }
26244                 fe.form = this;
26245                 if (fe.store) {
26246                     fe.store.form = this;
26247                 }
26248                 if (fe.isLayout) {  
26249                          
26250                     this.start(fe);
26251                     this.allItems.push(fe);
26252                     if (fe.items && fe.addxtype) {
26253                         fe.addxtype.apply(fe, fe.items);
26254                         delete fe.items;
26255                     }
26256                      this.end();
26257                     continue;
26258                 }
26259                 
26260                 
26261                  
26262                 this.add(fe);
26263               //  console.log('adding ' + ar[i].xtype);
26264             }
26265             if (ar[i].xtype == 'Button') {  
26266                 //console.log('adding button');
26267                 //console.log(ar[i]);
26268                 this.addButton(ar[i]);
26269                 this.allItems.push(fe);
26270                 continue;
26271             }
26272             
26273             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26274                 alert('end is not supported on xtype any more, use items');
26275             //    this.end();
26276             //    //console.log('adding end');
26277             }
26278             
26279         }
26280         return ret;
26281     },
26282     
26283     /**
26284      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26285      * option "monitorValid"
26286      */
26287     startMonitoring : function(){
26288         if(!this.bound){
26289             this.bound = true;
26290             Roo.TaskMgr.start({
26291                 run : this.bindHandler,
26292                 interval : this.monitorPoll || 200,
26293                 scope: this
26294             });
26295         }
26296     },
26297
26298     /**
26299      * Stops monitoring of the valid state of this form
26300      */
26301     stopMonitoring : function(){
26302         this.bound = false;
26303     },
26304
26305     // private
26306     bindHandler : function(){
26307         if(!this.bound){
26308             return false; // stops binding
26309         }
26310         var valid = true;
26311         this.items.each(function(f){
26312             if(!f.isValid(true)){
26313                 valid = false;
26314                 return false;
26315             }
26316         });
26317         for(var i = 0, len = this.buttons.length; i < len; i++){
26318             var btn = this.buttons[i];
26319             if(btn.formBind === true && btn.disabled === valid){
26320                 btn.setDisabled(!valid);
26321             }
26322         }
26323         this.fireEvent('clientvalidation', this, valid);
26324     }
26325     
26326     
26327     
26328     
26329     
26330     
26331     
26332     
26333 });
26334
26335
26336 // back compat
26337 Roo.Form = Roo.form.Form;
26338 /*
26339  * Based on:
26340  * Ext JS Library 1.1.1
26341  * Copyright(c) 2006-2007, Ext JS, LLC.
26342  *
26343  * Originally Released Under LGPL - original licence link has changed is not relivant.
26344  *
26345  * Fork - LGPL
26346  * <script type="text/javascript">
26347  */
26348  
26349  /**
26350  * @class Roo.form.Action
26351  * Internal Class used to handle form actions
26352  * @constructor
26353  * @param {Roo.form.BasicForm} el The form element or its id
26354  * @param {Object} config Configuration options
26355  */
26356  
26357  
26358 // define the action interface
26359 Roo.form.Action = function(form, options){
26360     this.form = form;
26361     this.options = options || {};
26362 };
26363 /**
26364  * Client Validation Failed
26365  * @const 
26366  */
26367 Roo.form.Action.CLIENT_INVALID = 'client';
26368 /**
26369  * Server Validation Failed
26370  * @const 
26371  */
26372  Roo.form.Action.SERVER_INVALID = 'server';
26373  /**
26374  * Connect to Server Failed
26375  * @const 
26376  */
26377 Roo.form.Action.CONNECT_FAILURE = 'connect';
26378 /**
26379  * Reading Data from Server Failed
26380  * @const 
26381  */
26382 Roo.form.Action.LOAD_FAILURE = 'load';
26383
26384 Roo.form.Action.prototype = {
26385     type : 'default',
26386     failureType : undefined,
26387     response : undefined,
26388     result : undefined,
26389
26390     // interface method
26391     run : function(options){
26392
26393     },
26394
26395     // interface method
26396     success : function(response){
26397
26398     },
26399
26400     // interface method
26401     handleResponse : function(response){
26402
26403     },
26404
26405     // default connection failure
26406     failure : function(response){
26407         this.response = response;
26408         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26409         this.form.afterAction(this, false);
26410     },
26411
26412     processResponse : function(response){
26413         this.response = response;
26414         if(!response.responseText){
26415             return true;
26416         }
26417         this.result = this.handleResponse(response);
26418         return this.result;
26419     },
26420
26421     // utility functions used internally
26422     getUrl : function(appendParams){
26423         var url = this.options.url || this.form.url || this.form.el.dom.action;
26424         if(appendParams){
26425             var p = this.getParams();
26426             if(p){
26427                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26428             }
26429         }
26430         return url;
26431     },
26432
26433     getMethod : function(){
26434         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26435     },
26436
26437     getParams : function(){
26438         var bp = this.form.baseParams;
26439         var p = this.options.params;
26440         if(p){
26441             if(typeof p == "object"){
26442                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26443             }else if(typeof p == 'string' && bp){
26444                 p += '&' + Roo.urlEncode(bp);
26445             }
26446         }else if(bp){
26447             p = Roo.urlEncode(bp);
26448         }
26449         return p;
26450     },
26451
26452     createCallback : function(){
26453         return {
26454             success: this.success,
26455             failure: this.failure,
26456             scope: this,
26457             timeout: (this.form.timeout*1000),
26458             upload: this.form.fileUpload ? this.success : undefined
26459         };
26460     }
26461 };
26462
26463 Roo.form.Action.Submit = function(form, options){
26464     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26465 };
26466
26467 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26468     type : 'submit',
26469
26470     run : function(){
26471         var o = this.options;
26472         var method = this.getMethod();
26473         var isPost = method == 'POST';
26474         if(o.clientValidation === false || this.form.isValid()){
26475             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26476                 form:this.form.el.dom,
26477                 url:this.getUrl(!isPost),
26478                 method: method,
26479                 params:isPost ? this.getParams() : null,
26480                 isUpload: this.form.fileUpload
26481             }));
26482
26483         }else if (o.clientValidation !== false){ // client validation failed
26484             this.failureType = Roo.form.Action.CLIENT_INVALID;
26485             this.form.afterAction(this, false);
26486         }
26487     },
26488
26489     success : function(response){
26490         var result = this.processResponse(response);
26491         if(result === true || result.success){
26492             this.form.afterAction(this, true);
26493             return;
26494         }
26495         if(result.errors){
26496             this.form.markInvalid(result.errors);
26497             this.failureType = Roo.form.Action.SERVER_INVALID;
26498         }
26499         this.form.afterAction(this, false);
26500     },
26501
26502     handleResponse : function(response){
26503         if(this.form.errorReader){
26504             var rs = this.form.errorReader.read(response);
26505             var errors = [];
26506             if(rs.records){
26507                 for(var i = 0, len = rs.records.length; i < len; i++) {
26508                     var r = rs.records[i];
26509                     errors[i] = r.data;
26510                 }
26511             }
26512             if(errors.length < 1){
26513                 errors = null;
26514             }
26515             return {
26516                 success : rs.success,
26517                 errors : errors
26518             };
26519         }
26520         var ret = false;
26521         try {
26522             ret = Roo.decode(response.responseText);
26523         } catch (e) {
26524             ret = {
26525                 success: false,
26526                 errorMsg: "Failed to read server message: " + response.responseText,
26527                 errors : []
26528             };
26529         }
26530         return ret;
26531         
26532     }
26533 });
26534
26535
26536 Roo.form.Action.Load = function(form, options){
26537     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26538     this.reader = this.form.reader;
26539 };
26540
26541 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26542     type : 'load',
26543
26544     run : function(){
26545         Roo.Ajax.request(Roo.apply(
26546                 this.createCallback(), {
26547                     method:this.getMethod(),
26548                     url:this.getUrl(false),
26549                     params:this.getParams()
26550         }));
26551     },
26552
26553     success : function(response){
26554         var result = this.processResponse(response);
26555         if(result === true || !result.success || !result.data){
26556             this.failureType = Roo.form.Action.LOAD_FAILURE;
26557             this.form.afterAction(this, false);
26558             return;
26559         }
26560         this.form.clearInvalid();
26561         this.form.setValues(result.data);
26562         this.form.afterAction(this, true);
26563     },
26564
26565     handleResponse : function(response){
26566         if(this.form.reader){
26567             var rs = this.form.reader.read(response);
26568             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26569             return {
26570                 success : rs.success,
26571                 data : data
26572             };
26573         }
26574         return Roo.decode(response.responseText);
26575     }
26576 });
26577
26578 Roo.form.Action.ACTION_TYPES = {
26579     'load' : Roo.form.Action.Load,
26580     'submit' : Roo.form.Action.Submit
26581 };/*
26582  * Based on:
26583  * Ext JS Library 1.1.1
26584  * Copyright(c) 2006-2007, Ext JS, LLC.
26585  *
26586  * Originally Released Under LGPL - original licence link has changed is not relivant.
26587  *
26588  * Fork - LGPL
26589  * <script type="text/javascript">
26590  */
26591  
26592 /**
26593  * @class Roo.form.Layout
26594  * @extends Roo.Component
26595  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26596  * @constructor
26597  * @param {Object} config Configuration options
26598  */
26599 Roo.form.Layout = function(config){
26600     var xitems = [];
26601     if (config.items) {
26602         xitems = config.items;
26603         delete config.items;
26604     }
26605     Roo.form.Layout.superclass.constructor.call(this, config);
26606     this.stack = [];
26607     Roo.each(xitems, this.addxtype, this);
26608      
26609 };
26610
26611 Roo.extend(Roo.form.Layout, Roo.Component, {
26612     /**
26613      * @cfg {String/Object} autoCreate
26614      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26615      */
26616     /**
26617      * @cfg {String/Object/Function} style
26618      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26619      * a function which returns such a specification.
26620      */
26621     /**
26622      * @cfg {String} labelAlign
26623      * Valid values are "left," "top" and "right" (defaults to "left")
26624      */
26625     /**
26626      * @cfg {Number} labelWidth
26627      * Fixed width in pixels of all field labels (defaults to undefined)
26628      */
26629     /**
26630      * @cfg {Boolean} clear
26631      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26632      */
26633     clear : true,
26634     /**
26635      * @cfg {String} labelSeparator
26636      * The separator to use after field labels (defaults to ':')
26637      */
26638     labelSeparator : ':',
26639     /**
26640      * @cfg {Boolean} hideLabels
26641      * True to suppress the display of field labels in this layout (defaults to false)
26642      */
26643     hideLabels : false,
26644
26645     // private
26646     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26647     
26648     isLayout : true,
26649     
26650     // private
26651     onRender : function(ct, position){
26652         if(this.el){ // from markup
26653             this.el = Roo.get(this.el);
26654         }else {  // generate
26655             var cfg = this.getAutoCreate();
26656             this.el = ct.createChild(cfg, position);
26657         }
26658         if(this.style){
26659             this.el.applyStyles(this.style);
26660         }
26661         if(this.labelAlign){
26662             this.el.addClass('x-form-label-'+this.labelAlign);
26663         }
26664         if(this.hideLabels){
26665             this.labelStyle = "display:none";
26666             this.elementStyle = "padding-left:0;";
26667         }else{
26668             if(typeof this.labelWidth == 'number'){
26669                 this.labelStyle = "width:"+this.labelWidth+"px;";
26670                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26671             }
26672             if(this.labelAlign == 'top'){
26673                 this.labelStyle = "width:auto;";
26674                 this.elementStyle = "padding-left:0;";
26675             }
26676         }
26677         var stack = this.stack;
26678         var slen = stack.length;
26679         if(slen > 0){
26680             if(!this.fieldTpl){
26681                 var t = new Roo.Template(
26682                     '<div class="x-form-item {5}">',
26683                         '<label for="{0}" style="{2}">{1}{4}</label>',
26684                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26685                         '</div>',
26686                     '</div><div class="x-form-clear-left"></div>'
26687                 );
26688                 t.disableFormats = true;
26689                 t.compile();
26690                 Roo.form.Layout.prototype.fieldTpl = t;
26691             }
26692             for(var i = 0; i < slen; i++) {
26693                 if(stack[i].isFormField){
26694                     this.renderField(stack[i]);
26695                 }else{
26696                     this.renderComponent(stack[i]);
26697                 }
26698             }
26699         }
26700         if(this.clear){
26701             this.el.createChild({cls:'x-form-clear'});
26702         }
26703     },
26704
26705     // private
26706     renderField : function(f){
26707         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26708                f.id, //0
26709                f.fieldLabel, //1
26710                f.labelStyle||this.labelStyle||'', //2
26711                this.elementStyle||'', //3
26712                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26713                f.itemCls||this.itemCls||''  //5
26714        ], true).getPrevSibling());
26715     },
26716
26717     // private
26718     renderComponent : function(c){
26719         c.render(c.isLayout ? this.el : this.el.createChild());    
26720     },
26721     /**
26722      * Adds a object form elements (using the xtype property as the factory method.)
26723      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26724      * @param {Object} config 
26725      */
26726     addxtype : function(o)
26727     {
26728         // create the lement.
26729         o.form = this.form;
26730         var fe = Roo.factory(o, Roo.form);
26731         this.form.allItems.push(fe);
26732         this.stack.push(fe);
26733         
26734         if (fe.isFormField) {
26735             this.form.items.add(fe);
26736         }
26737          
26738         return fe;
26739     }
26740 });
26741
26742 /**
26743  * @class Roo.form.Column
26744  * @extends Roo.form.Layout
26745  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26746  * @constructor
26747  * @param {Object} config Configuration options
26748  */
26749 Roo.form.Column = function(config){
26750     Roo.form.Column.superclass.constructor.call(this, config);
26751 };
26752
26753 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26754     /**
26755      * @cfg {Number/String} width
26756      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26757      */
26758     /**
26759      * @cfg {String/Object} autoCreate
26760      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26761      */
26762
26763     // private
26764     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26765
26766     // private
26767     onRender : function(ct, position){
26768         Roo.form.Column.superclass.onRender.call(this, ct, position);
26769         if(this.width){
26770             this.el.setWidth(this.width);
26771         }
26772     }
26773 });
26774
26775
26776 /**
26777  * @class Roo.form.Row
26778  * @extends Roo.form.Layout
26779  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26780  * @constructor
26781  * @param {Object} config Configuration options
26782  */
26783
26784  
26785 Roo.form.Row = function(config){
26786     Roo.form.Row.superclass.constructor.call(this, config);
26787 };
26788  
26789 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26790       /**
26791      * @cfg {Number/String} width
26792      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26793      */
26794     /**
26795      * @cfg {Number/String} height
26796      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26797      */
26798     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26799     
26800     padWidth : 20,
26801     // private
26802     onRender : function(ct, position){
26803         //console.log('row render');
26804         if(!this.rowTpl){
26805             var t = new Roo.Template(
26806                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26807                     '<label for="{0}" style="{2}">{1}{4}</label>',
26808                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26809                     '</div>',
26810                 '</div>'
26811             );
26812             t.disableFormats = true;
26813             t.compile();
26814             Roo.form.Layout.prototype.rowTpl = t;
26815         }
26816         this.fieldTpl = this.rowTpl;
26817         
26818         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26819         var labelWidth = 100;
26820         
26821         if ((this.labelAlign != 'top')) {
26822             if (typeof this.labelWidth == 'number') {
26823                 labelWidth = this.labelWidth
26824             }
26825             this.padWidth =  20 + labelWidth;
26826             
26827         }
26828         
26829         Roo.form.Column.superclass.onRender.call(this, ct, position);
26830         if(this.width){
26831             this.el.setWidth(this.width);
26832         }
26833         if(this.height){
26834             this.el.setHeight(this.height);
26835         }
26836     },
26837     
26838     // private
26839     renderField : function(f){
26840         f.fieldEl = this.fieldTpl.append(this.el, [
26841                f.id, f.fieldLabel,
26842                f.labelStyle||this.labelStyle||'',
26843                this.elementStyle||'',
26844                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26845                f.itemCls||this.itemCls||'',
26846                f.width ? f.width + this.padWidth : 160 + this.padWidth
26847        ],true);
26848     }
26849 });
26850  
26851
26852 /**
26853  * @class Roo.form.FieldSet
26854  * @extends Roo.form.Layout
26855  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26856  * @constructor
26857  * @param {Object} config Configuration options
26858  */
26859 Roo.form.FieldSet = function(config){
26860     Roo.form.FieldSet.superclass.constructor.call(this, config);
26861 };
26862
26863 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26864     /**
26865      * @cfg {String} legend
26866      * The text to display as the legend for the FieldSet (defaults to '')
26867      */
26868     /**
26869      * @cfg {String/Object} autoCreate
26870      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26871      */
26872
26873     // private
26874     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26875
26876     // private
26877     onRender : function(ct, position){
26878         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26879         if(this.legend){
26880             this.setLegend(this.legend);
26881         }
26882     },
26883
26884     // private
26885     setLegend : function(text){
26886         if(this.rendered){
26887             this.el.child('legend').update(text);
26888         }
26889     }
26890 });/*
26891  * Based on:
26892  * Ext JS Library 1.1.1
26893  * Copyright(c) 2006-2007, Ext JS, LLC.
26894  *
26895  * Originally Released Under LGPL - original licence link has changed is not relivant.
26896  *
26897  * Fork - LGPL
26898  * <script type="text/javascript">
26899  */
26900 /**
26901  * @class Roo.form.VTypes
26902  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26903  * @singleton
26904  */
26905 Roo.form.VTypes = function(){
26906     // closure these in so they are only created once.
26907     var alpha = /^[a-zA-Z_]+$/;
26908     var alphanum = /^[a-zA-Z0-9_]+$/;
26909     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
26910     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26911
26912     // All these messages and functions are configurable
26913     return {
26914         /**
26915          * The function used to validate email addresses
26916          * @param {String} value The email address
26917          */
26918         'email' : function(v){
26919             return email.test(v);
26920         },
26921         /**
26922          * The error text to display when the email validation function returns false
26923          * @type String
26924          */
26925         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26926         /**
26927          * The keystroke filter mask to be applied on email input
26928          * @type RegExp
26929          */
26930         'emailMask' : /[a-z0-9_\.\-@]/i,
26931
26932         /**
26933          * The function used to validate URLs
26934          * @param {String} value The URL
26935          */
26936         'url' : function(v){
26937             return url.test(v);
26938         },
26939         /**
26940          * The error text to display when the url validation function returns false
26941          * @type String
26942          */
26943         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26944         
26945         /**
26946          * The function used to validate alpha values
26947          * @param {String} value The value
26948          */
26949         'alpha' : function(v){
26950             return alpha.test(v);
26951         },
26952         /**
26953          * The error text to display when the alpha validation function returns false
26954          * @type String
26955          */
26956         'alphaText' : 'This field should only contain letters and _',
26957         /**
26958          * The keystroke filter mask to be applied on alpha input
26959          * @type RegExp
26960          */
26961         'alphaMask' : /[a-z_]/i,
26962
26963         /**
26964          * The function used to validate alphanumeric values
26965          * @param {String} value The value
26966          */
26967         'alphanum' : function(v){
26968             return alphanum.test(v);
26969         },
26970         /**
26971          * The error text to display when the alphanumeric validation function returns false
26972          * @type String
26973          */
26974         'alphanumText' : 'This field should only contain letters, numbers and _',
26975         /**
26976          * The keystroke filter mask to be applied on alphanumeric input
26977          * @type RegExp
26978          */
26979         'alphanumMask' : /[a-z0-9_]/i
26980     };
26981 }();//<script type="text/javascript">
26982
26983 /**
26984  * @class Roo.form.FCKeditor
26985  * @extends Roo.form.TextArea
26986  * Wrapper around the FCKEditor http://www.fckeditor.net
26987  * @constructor
26988  * Creates a new FCKeditor
26989  * @param {Object} config Configuration options
26990  */
26991 Roo.form.FCKeditor = function(config){
26992     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26993     this.addEvents({
26994          /**
26995          * @event editorinit
26996          * Fired when the editor is initialized - you can add extra handlers here..
26997          * @param {FCKeditor} this
26998          * @param {Object} the FCK object.
26999          */
27000         editorinit : true
27001     });
27002     
27003     
27004 };
27005 Roo.form.FCKeditor.editors = { };
27006 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27007 {
27008     //defaultAutoCreate : {
27009     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27010     //},
27011     // private
27012     /**
27013      * @cfg {Object} fck options - see fck manual for details.
27014      */
27015     fckconfig : false,
27016     
27017     /**
27018      * @cfg {Object} fck toolbar set (Basic or Default)
27019      */
27020     toolbarSet : 'Basic',
27021     /**
27022      * @cfg {Object} fck BasePath
27023      */ 
27024     basePath : '/fckeditor/',
27025     
27026     
27027     frame : false,
27028     
27029     value : '',
27030     
27031    
27032     onRender : function(ct, position)
27033     {
27034         if(!this.el){
27035             this.defaultAutoCreate = {
27036                 tag: "textarea",
27037                 style:"width:300px;height:60px;",
27038                 autocomplete: "off"
27039             };
27040         }
27041         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27042         /*
27043         if(this.grow){
27044             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27045             if(this.preventScrollbars){
27046                 this.el.setStyle("overflow", "hidden");
27047             }
27048             this.el.setHeight(this.growMin);
27049         }
27050         */
27051         //console.log('onrender' + this.getId() );
27052         Roo.form.FCKeditor.editors[this.getId()] = this;
27053          
27054
27055         this.replaceTextarea() ;
27056         
27057     },
27058     
27059     getEditor : function() {
27060         return this.fckEditor;
27061     },
27062     /**
27063      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27064      * @param {Mixed} value The value to set
27065      */
27066     
27067     
27068     setValue : function(value)
27069     {
27070         //console.log('setValue: ' + value);
27071         
27072         if(typeof(value) == 'undefined') { // not sure why this is happending...
27073             return;
27074         }
27075         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27076         
27077         //if(!this.el || !this.getEditor()) {
27078         //    this.value = value;
27079             //this.setValue.defer(100,this,[value]);    
27080         //    return;
27081         //} 
27082         
27083         if(!this.getEditor()) {
27084             return;
27085         }
27086         
27087         this.getEditor().SetData(value);
27088         
27089         //
27090
27091     },
27092
27093     /**
27094      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27095      * @return {Mixed} value The field value
27096      */
27097     getValue : function()
27098     {
27099         
27100         if (this.frame && this.frame.dom.style.display == 'none') {
27101             return Roo.form.FCKeditor.superclass.getValue.call(this);
27102         }
27103         
27104         if(!this.el || !this.getEditor()) {
27105            
27106            // this.getValue.defer(100,this); 
27107             return this.value;
27108         }
27109        
27110         
27111         var value=this.getEditor().GetData();
27112         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27113         return Roo.form.FCKeditor.superclass.getValue.call(this);
27114         
27115
27116     },
27117
27118     /**
27119      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27120      * @return {Mixed} value The field value
27121      */
27122     getRawValue : function()
27123     {
27124         if (this.frame && this.frame.dom.style.display == 'none') {
27125             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27126         }
27127         
27128         if(!this.el || !this.getEditor()) {
27129             //this.getRawValue.defer(100,this); 
27130             return this.value;
27131             return;
27132         }
27133         
27134         
27135         
27136         var value=this.getEditor().GetData();
27137         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27138         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27139          
27140     },
27141     
27142     setSize : function(w,h) {
27143         
27144         
27145         
27146         //if (this.frame && this.frame.dom.style.display == 'none') {
27147         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27148         //    return;
27149         //}
27150         //if(!this.el || !this.getEditor()) {
27151         //    this.setSize.defer(100,this, [w,h]); 
27152         //    return;
27153         //}
27154         
27155         
27156         
27157         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27158         
27159         this.frame.dom.setAttribute('width', w);
27160         this.frame.dom.setAttribute('height', h);
27161         this.frame.setSize(w,h);
27162         
27163     },
27164     
27165     toggleSourceEdit : function(value) {
27166         
27167       
27168          
27169         this.el.dom.style.display = value ? '' : 'none';
27170         this.frame.dom.style.display = value ?  'none' : '';
27171         
27172     },
27173     
27174     
27175     focus: function(tag)
27176     {
27177         if (this.frame.dom.style.display == 'none') {
27178             return Roo.form.FCKeditor.superclass.focus.call(this);
27179         }
27180         if(!this.el || !this.getEditor()) {
27181             this.focus.defer(100,this, [tag]); 
27182             return;
27183         }
27184         
27185         
27186         
27187         
27188         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27189         this.getEditor().Focus();
27190         if (tgs.length) {
27191             if (!this.getEditor().Selection.GetSelection()) {
27192                 this.focus.defer(100,this, [tag]); 
27193                 return;
27194             }
27195             
27196             
27197             var r = this.getEditor().EditorDocument.createRange();
27198             r.setStart(tgs[0],0);
27199             r.setEnd(tgs[0],0);
27200             this.getEditor().Selection.GetSelection().removeAllRanges();
27201             this.getEditor().Selection.GetSelection().addRange(r);
27202             this.getEditor().Focus();
27203         }
27204         
27205     },
27206     
27207     
27208     
27209     replaceTextarea : function()
27210     {
27211         if ( document.getElementById( this.getId() + '___Frame' ) )
27212             return ;
27213         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27214         //{
27215             // We must check the elements firstly using the Id and then the name.
27216         var oTextarea = document.getElementById( this.getId() );
27217         
27218         var colElementsByName = document.getElementsByName( this.getId() ) ;
27219          
27220         oTextarea.style.display = 'none' ;
27221
27222         if ( oTextarea.tabIndex ) {            
27223             this.TabIndex = oTextarea.tabIndex ;
27224         }
27225         
27226         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27227         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27228         this.frame = Roo.get(this.getId() + '___Frame')
27229     },
27230     
27231     _getConfigHtml : function()
27232     {
27233         var sConfig = '' ;
27234
27235         for ( var o in this.fckconfig ) {
27236             sConfig += sConfig.length > 0  ? '&amp;' : '';
27237             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27238         }
27239
27240         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27241     },
27242     
27243     
27244     _getIFrameHtml : function()
27245     {
27246         var sFile = 'fckeditor.html' ;
27247         /* no idea what this is about..
27248         try
27249         {
27250             if ( (/fcksource=true/i).test( window.top.location.search ) )
27251                 sFile = 'fckeditor.original.html' ;
27252         }
27253         catch (e) { 
27254         */
27255
27256         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27257         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27258         
27259         
27260         var html = '<iframe id="' + this.getId() +
27261             '___Frame" src="' + sLink +
27262             '" width="' + this.width +
27263             '" height="' + this.height + '"' +
27264             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27265             ' frameborder="0" scrolling="no"></iframe>' ;
27266
27267         return html ;
27268     },
27269     
27270     _insertHtmlBefore : function( html, element )
27271     {
27272         if ( element.insertAdjacentHTML )       {
27273             // IE
27274             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27275         } else { // Gecko
27276             var oRange = document.createRange() ;
27277             oRange.setStartBefore( element ) ;
27278             var oFragment = oRange.createContextualFragment( html );
27279             element.parentNode.insertBefore( oFragment, element ) ;
27280         }
27281     }
27282     
27283     
27284   
27285     
27286     
27287     
27288     
27289
27290 });
27291
27292 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27293
27294 function FCKeditor_OnComplete(editorInstance){
27295     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27296     f.fckEditor = editorInstance;
27297     //console.log("loaded");
27298     f.fireEvent('editorinit', f, editorInstance);
27299
27300   
27301
27302  
27303
27304
27305
27306
27307
27308
27309
27310
27311
27312
27313
27314
27315
27316
27317
27318 //<script type="text/javascript">
27319 /**
27320  * @class Roo.form.GridField
27321  * @extends Roo.form.Field
27322  * Embed a grid (or editable grid into a form)
27323  * STATUS ALPHA
27324  * @constructor
27325  * Creates a new GridField
27326  * @param {Object} config Configuration options
27327  */
27328 Roo.form.GridField = function(config){
27329     Roo.form.GridField.superclass.constructor.call(this, config);
27330      
27331 };
27332
27333 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27334     /**
27335      * @cfg {Number} width  - used to restrict width of grid..
27336      */
27337     width : 100,
27338     /**
27339      * @cfg {Number} height - used to restrict height of grid..
27340      */
27341     height : 50,
27342      /**
27343      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
27344      */
27345     xgrid : false, 
27346     /**
27347      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27348      * {tag: "input", type: "checkbox", autocomplete: "off"})
27349      */
27350    // defaultAutoCreate : { tag: 'div' },
27351     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27352     /**
27353      * @cfg {String} addTitle Text to include for adding a title.
27354      */
27355     addTitle : false,
27356     //
27357     onResize : function(){
27358         Roo.form.Field.superclass.onResize.apply(this, arguments);
27359     },
27360
27361     initEvents : function(){
27362         // Roo.form.Checkbox.superclass.initEvents.call(this);
27363         // has no events...
27364        
27365     },
27366
27367
27368     getResizeEl : function(){
27369         return this.wrap;
27370     },
27371
27372     getPositionEl : function(){
27373         return this.wrap;
27374     },
27375
27376     // private
27377     onRender : function(ct, position){
27378         
27379         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27380         var style = this.style;
27381         delete this.style;
27382         
27383         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
27384         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27385         this.viewEl = this.wrap.createChild({ tag: 'div' });
27386         if (style) {
27387             this.viewEl.applyStyles(style);
27388         }
27389         if (this.width) {
27390             this.viewEl.setWidth(this.width);
27391         }
27392         if (this.height) {
27393             this.viewEl.setHeight(this.height);
27394         }
27395         //if(this.inputValue !== undefined){
27396         //this.setValue(this.value);
27397         
27398         
27399         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27400         
27401         
27402         this.grid.render();
27403         this.grid.getDataSource().on('remove', this.refreshValue, this);
27404         this.grid.getDataSource().on('update', this.refreshValue, this);
27405         this.grid.on('afteredit', this.refreshValue, this);
27406  
27407     },
27408      
27409     
27410     /**
27411      * Sets the value of the item. 
27412      * @param {String} either an object  or a string..
27413      */
27414     setValue : function(v){
27415         //this.value = v;
27416         v = v || []; // empty set..
27417         // this does not seem smart - it really only affects memoryproxy grids..
27418         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27419             var ds = this.grid.getDataSource();
27420             // assumes a json reader..
27421             var data = {}
27422             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27423             ds.loadData( data);
27424         }
27425         Roo.form.GridField.superclass.setValue.call(this, v);
27426         this.refreshValue();
27427         // should load data in the grid really....
27428     },
27429     
27430     // private
27431     refreshValue: function() {
27432          var val = [];
27433         this.grid.getDataSource().each(function(r) {
27434             val.push(r.data);
27435         });
27436         this.el.dom.value = Roo.encode(val);
27437     }
27438     
27439      
27440     
27441     
27442 });//<script type="text/javasscript">
27443  
27444
27445 /**
27446  * @class Roo.DDView
27447  * A DnD enabled version of Roo.View.
27448  * @param {Element/String} container The Element in which to create the View.
27449  * @param {String} tpl The template string used to create the markup for each element of the View
27450  * @param {Object} config The configuration properties. These include all the config options of
27451  * {@link Roo.View} plus some specific to this class.<br>
27452  * <p>
27453  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
27454  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
27455  * <p>
27456  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
27457 .x-view-drag-insert-above {
27458         border-top:1px dotted #3366cc;
27459 }
27460 .x-view-drag-insert-below {
27461         border-bottom:1px dotted #3366cc;
27462 }
27463 </code></pre>
27464  * 
27465  */
27466  
27467 Roo.DDView = function(container, tpl, config) {
27468     Roo.DDView.superclass.constructor.apply(this, arguments);
27469     this.getEl().setStyle("outline", "0px none");
27470     this.getEl().unselectable();
27471     if (this.dragGroup) {
27472                 this.setDraggable(this.dragGroup.split(","));
27473     }
27474     if (this.dropGroup) {
27475                 this.setDroppable(this.dropGroup.split(","));
27476     }
27477     if (this.deletable) {
27478         this.setDeletable();
27479     }
27480     this.isDirtyFlag = false;
27481         this.addEvents({
27482                 "drop" : true
27483         });
27484 };
27485
27486 Roo.extend(Roo.DDView, Roo.View, {
27487 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
27488 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
27489 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
27490 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
27491
27492         isFormField: true,
27493
27494         reset: Roo.emptyFn,
27495         
27496         clearInvalid: Roo.form.Field.prototype.clearInvalid,
27497
27498         validate: function() {
27499                 return true;
27500         },
27501         
27502         destroy: function() {
27503                 this.purgeListeners();
27504                 this.getEl.removeAllListeners();
27505                 this.getEl().remove();
27506                 if (this.dragZone) {
27507                         if (this.dragZone.destroy) {
27508                                 this.dragZone.destroy();
27509                         }
27510                 }
27511                 if (this.dropZone) {
27512                         if (this.dropZone.destroy) {
27513                                 this.dropZone.destroy();
27514                         }
27515                 }
27516         },
27517
27518 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
27519         getName: function() {
27520                 return this.name;
27521         },
27522
27523 /**     Loads the View from a JSON string representing the Records to put into the Store. */
27524         setValue: function(v) {
27525                 if (!this.store) {
27526                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
27527                 }
27528                 var data = {};
27529                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
27530                 this.store.proxy = new Roo.data.MemoryProxy(data);
27531                 this.store.load();
27532         },
27533
27534 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
27535         getValue: function() {
27536                 var result = '(';
27537                 this.store.each(function(rec) {
27538                         result += rec.id + ',';
27539                 });
27540                 return result.substr(0, result.length - 1) + ')';
27541         },
27542         
27543         getIds: function() {
27544                 var i = 0, result = new Array(this.store.getCount());
27545                 this.store.each(function(rec) {
27546                         result[i++] = rec.id;
27547                 });
27548                 return result;
27549         },
27550         
27551         isDirty: function() {
27552                 return this.isDirtyFlag;
27553         },
27554
27555 /**
27556  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
27557  *      whole Element becomes the target, and this causes the drop gesture to append.
27558  */
27559     getTargetFromEvent : function(e) {
27560                 var target = e.getTarget();
27561                 while ((target !== null) && (target.parentNode != this.el.dom)) {
27562                 target = target.parentNode;
27563                 }
27564                 if (!target) {
27565                         target = this.el.dom.lastChild || this.el.dom;
27566                 }
27567                 return target;
27568     },
27569
27570 /**
27571  *      Create the drag data which consists of an object which has the property "ddel" as
27572  *      the drag proxy element. 
27573  */
27574     getDragData : function(e) {
27575         var target = this.findItemFromChild(e.getTarget());
27576                 if(target) {
27577                         this.handleSelection(e);
27578                         var selNodes = this.getSelectedNodes();
27579             var dragData = {
27580                 source: this,
27581                 copy: this.copy || (this.allowCopy && e.ctrlKey),
27582                 nodes: selNodes,
27583                 records: []
27584                         };
27585                         var selectedIndices = this.getSelectedIndexes();
27586                         for (var i = 0; i < selectedIndices.length; i++) {
27587                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
27588                         }
27589                         if (selNodes.length == 1) {
27590                                 dragData.ddel = target.cloneNode(true); // the div element
27591                         } else {
27592                                 var div = document.createElement('div'); // create the multi element drag "ghost"
27593                                 div.className = 'multi-proxy';
27594                                 for (var i = 0, len = selNodes.length; i < len; i++) {
27595                                         div.appendChild(selNodes[i].cloneNode(true));
27596                                 }
27597                                 dragData.ddel = div;
27598                         }
27599             //console.log(dragData)
27600             //console.log(dragData.ddel.innerHTML)
27601                         return dragData;
27602                 }
27603         //console.log('nodragData')
27604                 return false;
27605     },
27606     
27607 /**     Specify to which ddGroup items in this DDView may be dragged. */
27608     setDraggable: function(ddGroup) {
27609         if (ddGroup instanceof Array) {
27610                 Roo.each(ddGroup, this.setDraggable, this);
27611                 return;
27612         }
27613         if (this.dragZone) {
27614                 this.dragZone.addToGroup(ddGroup);
27615         } else {
27616                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
27617                                 containerScroll: true,
27618                                 ddGroup: ddGroup 
27619
27620                         });
27621 //                      Draggability implies selection. DragZone's mousedown selects the element.
27622                         if (!this.multiSelect) { this.singleSelect = true; }
27623
27624 //                      Wire the DragZone's handlers up to methods in *this*
27625                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
27626                 }
27627     },
27628
27629 /**     Specify from which ddGroup this DDView accepts drops. */
27630     setDroppable: function(ddGroup) {
27631         if (ddGroup instanceof Array) {
27632                 Roo.each(ddGroup, this.setDroppable, this);
27633                 return;
27634         }
27635         if (this.dropZone) {
27636                 this.dropZone.addToGroup(ddGroup);
27637         } else {
27638                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
27639                                 containerScroll: true,
27640                                 ddGroup: ddGroup
27641                         });
27642
27643 //                      Wire the DropZone's handlers up to methods in *this*
27644                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
27645                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
27646                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
27647                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
27648                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
27649                 }
27650     },
27651
27652 /**     Decide whether to drop above or below a View node. */
27653     getDropPoint : function(e, n, dd){
27654         if (n == this.el.dom) { return "above"; }
27655                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
27656                 var c = t + (b - t) / 2;
27657                 var y = Roo.lib.Event.getPageY(e);
27658                 if(y <= c) {
27659                         return "above";
27660                 }else{
27661                         return "below";
27662                 }
27663     },
27664
27665     onNodeEnter : function(n, dd, e, data){
27666                 return false;
27667     },
27668     
27669     onNodeOver : function(n, dd, e, data){
27670                 var pt = this.getDropPoint(e, n, dd);
27671                 // set the insert point style on the target node
27672                 var dragElClass = this.dropNotAllowed;
27673                 if (pt) {
27674                         var targetElClass;
27675                         if (pt == "above"){
27676                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
27677                                 targetElClass = "x-view-drag-insert-above";
27678                         } else {
27679                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
27680                                 targetElClass = "x-view-drag-insert-below";
27681                         }
27682                         if (this.lastInsertClass != targetElClass){
27683                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
27684                                 this.lastInsertClass = targetElClass;
27685                         }
27686                 }
27687                 return dragElClass;
27688         },
27689
27690     onNodeOut : function(n, dd, e, data){
27691                 this.removeDropIndicators(n);
27692     },
27693
27694     onNodeDrop : function(n, dd, e, data){
27695         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
27696                 return false;
27697         }
27698         var pt = this.getDropPoint(e, n, dd);
27699                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
27700                 if (pt == "below") { insertAt++; }
27701                 for (var i = 0; i < data.records.length; i++) {
27702                         var r = data.records[i];
27703                         var dup = this.store.getById(r.id);
27704                         if (dup && (dd != this.dragZone)) {
27705                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
27706                         } else {
27707                                 if (data.copy) {
27708                                         this.store.insert(insertAt++, r.copy());
27709                                 } else {
27710                                         data.source.isDirtyFlag = true;
27711                                         r.store.remove(r);
27712                                         this.store.insert(insertAt++, r);
27713                                 }
27714                                 this.isDirtyFlag = true;
27715                         }
27716                 }
27717                 this.dragZone.cachedTarget = null;
27718                 return true;
27719     },
27720
27721     removeDropIndicators : function(n){
27722                 if(n){
27723                         Roo.fly(n).removeClass([
27724                                 "x-view-drag-insert-above",
27725                                 "x-view-drag-insert-below"]);
27726                         this.lastInsertClass = "_noclass";
27727                 }
27728     },
27729
27730 /**
27731  *      Utility method. Add a delete option to the DDView's context menu.
27732  *      @param {String} imageUrl The URL of the "delete" icon image.
27733  */
27734         setDeletable: function(imageUrl) {
27735                 if (!this.singleSelect && !this.multiSelect) {
27736                         this.singleSelect = true;
27737                 }
27738                 var c = this.getContextMenu();
27739                 this.contextMenu.on("itemclick", function(item) {
27740                         switch (item.id) {
27741                                 case "delete":
27742                                         this.remove(this.getSelectedIndexes());
27743                                         break;
27744                         }
27745                 }, this);
27746                 this.contextMenu.add({
27747                         icon: imageUrl,
27748                         id: "delete",
27749                         text: 'Delete'
27750                 });
27751         },
27752         
27753 /**     Return the context menu for this DDView. */
27754         getContextMenu: function() {
27755                 if (!this.contextMenu) {
27756 //                      Create the View's context menu
27757                         this.contextMenu = new Roo.menu.Menu({
27758                                 id: this.id + "-contextmenu"
27759                         });
27760                         this.el.on("contextmenu", this.showContextMenu, this);
27761                 }
27762                 return this.contextMenu;
27763         },
27764         
27765         disableContextMenu: function() {
27766                 if (this.contextMenu) {
27767                         this.el.un("contextmenu", this.showContextMenu, this);
27768                 }
27769         },
27770
27771         showContextMenu: function(e, item) {
27772         item = this.findItemFromChild(e.getTarget());
27773                 if (item) {
27774                         e.stopEvent();
27775                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
27776                         this.contextMenu.showAt(e.getXY());
27777             }
27778     },
27779
27780 /**
27781  *      Remove {@link Roo.data.Record}s at the specified indices.
27782  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
27783  */
27784     remove: function(selectedIndices) {
27785                 selectedIndices = [].concat(selectedIndices);
27786                 for (var i = 0; i < selectedIndices.length; i++) {
27787                         var rec = this.store.getAt(selectedIndices[i]);
27788                         this.store.remove(rec);
27789                 }
27790     },
27791
27792 /**
27793  *      Double click fires the event, but also, if this is draggable, and there is only one other
27794  *      related DropZone, it transfers the selected node.
27795  */
27796     onDblClick : function(e){
27797         var item = this.findItemFromChild(e.getTarget());
27798         if(item){
27799             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
27800                 return false;
27801             }
27802             if (this.dragGroup) {
27803                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
27804                     while (targets.indexOf(this.dropZone) > -1) {
27805                             targets.remove(this.dropZone);
27806                                 }
27807                     if (targets.length == 1) {
27808                                         this.dragZone.cachedTarget = null;
27809                         var el = Roo.get(targets[0].getEl());
27810                         var box = el.getBox(true);
27811                         targets[0].onNodeDrop(el.dom, {
27812                                 target: el.dom,
27813                                 xy: [box.x, box.y + box.height - 1]
27814                         }, null, this.getDragData(e));
27815                     }
27816                 }
27817         }
27818     },
27819     
27820     handleSelection: function(e) {
27821                 this.dragZone.cachedTarget = null;
27822         var item = this.findItemFromChild(e.getTarget());
27823         if (!item) {
27824                 this.clearSelections(true);
27825                 return;
27826         }
27827                 if (item && (this.multiSelect || this.singleSelect)){
27828                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
27829                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
27830                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
27831                                 this.unselect(item);
27832                         } else {
27833                                 this.select(item, this.multiSelect && e.ctrlKey);
27834                                 this.lastSelection = item;
27835                         }
27836                 }
27837     },
27838
27839     onItemClick : function(item, index, e){
27840                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
27841                         return false;
27842                 }
27843                 return true;
27844     },
27845
27846     unselect : function(nodeInfo, suppressEvent){
27847                 var node = this.getNode(nodeInfo);
27848                 if(node && this.isSelected(node)){
27849                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27850                                 Roo.fly(node).removeClass(this.selectedClass);
27851                                 this.selections.remove(node);
27852                                 if(!suppressEvent){
27853                                         this.fireEvent("selectionchange", this, this.selections);
27854                                 }
27855                         }
27856                 }
27857     }
27858 });
27859 /*
27860  * Based on:
27861  * Ext JS Library 1.1.1
27862  * Copyright(c) 2006-2007, Ext JS, LLC.
27863  *
27864  * Originally Released Under LGPL - original licence link has changed is not relivant.
27865  *
27866  * Fork - LGPL
27867  * <script type="text/javascript">
27868  */
27869  
27870 /**
27871  * @class Roo.LayoutManager
27872  * @extends Roo.util.Observable
27873  * Base class for layout managers.
27874  */
27875 Roo.LayoutManager = function(container, config){
27876     Roo.LayoutManager.superclass.constructor.call(this);
27877     this.el = Roo.get(container);
27878     // ie scrollbar fix
27879     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
27880         document.body.scroll = "no";
27881     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
27882         this.el.position('relative');
27883     }
27884     this.id = this.el.id;
27885     this.el.addClass("x-layout-container");
27886     /** false to disable window resize monitoring @type Boolean */
27887     this.monitorWindowResize = true;
27888     this.regions = {};
27889     this.addEvents({
27890         /**
27891          * @event layout
27892          * Fires when a layout is performed. 
27893          * @param {Roo.LayoutManager} this
27894          */
27895         "layout" : true,
27896         /**
27897          * @event regionresized
27898          * Fires when the user resizes a region. 
27899          * @param {Roo.LayoutRegion} region The resized region
27900          * @param {Number} newSize The new size (width for east/west, height for north/south)
27901          */
27902         "regionresized" : true,
27903         /**
27904          * @event regioncollapsed
27905          * Fires when a region is collapsed. 
27906          * @param {Roo.LayoutRegion} region The collapsed region
27907          */
27908         "regioncollapsed" : true,
27909         /**
27910          * @event regionexpanded
27911          * Fires when a region is expanded.  
27912          * @param {Roo.LayoutRegion} region The expanded region
27913          */
27914         "regionexpanded" : true
27915     });
27916     this.updating = false;
27917     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
27918 };
27919
27920 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
27921     /**
27922      * Returns true if this layout is currently being updated
27923      * @return {Boolean}
27924      */
27925     isUpdating : function(){
27926         return this.updating; 
27927     },
27928     
27929     /**
27930      * Suspend the LayoutManager from doing auto-layouts while
27931      * making multiple add or remove calls
27932      */
27933     beginUpdate : function(){
27934         this.updating = true;    
27935     },
27936     
27937     /**
27938      * Restore auto-layouts and optionally disable the manager from performing a layout
27939      * @param {Boolean} noLayout true to disable a layout update 
27940      */
27941     endUpdate : function(noLayout){
27942         this.updating = false;
27943         if(!noLayout){
27944             this.layout();
27945         }    
27946     },
27947     
27948     layout: function(){
27949         
27950     },
27951     
27952     onRegionResized : function(region, newSize){
27953         this.fireEvent("regionresized", region, newSize);
27954         this.layout();
27955     },
27956     
27957     onRegionCollapsed : function(region){
27958         this.fireEvent("regioncollapsed", region);
27959     },
27960     
27961     onRegionExpanded : function(region){
27962         this.fireEvent("regionexpanded", region);
27963     },
27964         
27965     /**
27966      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
27967      * performs box-model adjustments.
27968      * @return {Object} The size as an object {width: (the width), height: (the height)}
27969      */
27970     getViewSize : function(){
27971         var size;
27972         if(this.el.dom != document.body){
27973             size = this.el.getSize();
27974         }else{
27975             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
27976         }
27977         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
27978         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27979         return size;
27980     },
27981     
27982     /**
27983      * Returns the Element this layout is bound to.
27984      * @return {Roo.Element}
27985      */
27986     getEl : function(){
27987         return this.el;
27988     },
27989     
27990     /**
27991      * Returns the specified region.
27992      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
27993      * @return {Roo.LayoutRegion}
27994      */
27995     getRegion : function(target){
27996         return this.regions[target.toLowerCase()];
27997     },
27998     
27999     onWindowResize : function(){
28000         if(this.monitorWindowResize){
28001             this.layout();
28002         }
28003     }
28004 });/*
28005  * Based on:
28006  * Ext JS Library 1.1.1
28007  * Copyright(c) 2006-2007, Ext JS, LLC.
28008  *
28009  * Originally Released Under LGPL - original licence link has changed is not relivant.
28010  *
28011  * Fork - LGPL
28012  * <script type="text/javascript">
28013  */
28014 /**
28015  * @class Roo.BorderLayout
28016  * @extends Roo.LayoutManager
28017  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28018  * please see: <br><br>
28019  * <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>
28020  * <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>
28021  * Example:
28022  <pre><code>
28023  var layout = new Roo.BorderLayout(document.body, {
28024     north: {
28025         initialSize: 25,
28026         titlebar: false
28027     },
28028     west: {
28029         split:true,
28030         initialSize: 200,
28031         minSize: 175,
28032         maxSize: 400,
28033         titlebar: true,
28034         collapsible: true
28035     },
28036     east: {
28037         split:true,
28038         initialSize: 202,
28039         minSize: 175,
28040         maxSize: 400,
28041         titlebar: true,
28042         collapsible: true
28043     },
28044     south: {
28045         split:true,
28046         initialSize: 100,
28047         minSize: 100,
28048         maxSize: 200,
28049         titlebar: true,
28050         collapsible: true
28051     },
28052     center: {
28053         titlebar: true,
28054         autoScroll:true,
28055         resizeTabs: true,
28056         minTabWidth: 50,
28057         preferredTabWidth: 150
28058     }
28059 });
28060
28061 // shorthand
28062 var CP = Roo.ContentPanel;
28063
28064 layout.beginUpdate();
28065 layout.add("north", new CP("north", "North"));
28066 layout.add("south", new CP("south", {title: "South", closable: true}));
28067 layout.add("west", new CP("west", {title: "West"}));
28068 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28069 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28070 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28071 layout.getRegion("center").showPanel("center1");
28072 layout.endUpdate();
28073 </code></pre>
28074
28075 <b>The container the layout is rendered into can be either the body element or any other element.
28076 If it is not the body element, the container needs to either be an absolute positioned element,
28077 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28078 the container size if it is not the body element.</b>
28079
28080 * @constructor
28081 * Create a new BorderLayout
28082 * @param {String/HTMLElement/Element} container The container this layout is bound to
28083 * @param {Object} config Configuration options
28084  */
28085 Roo.BorderLayout = function(container, config){
28086     config = config || {};
28087     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28088     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28089     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28090         var target = this.factory.validRegions[i];
28091         if(config[target]){
28092             this.addRegion(target, config[target]);
28093         }
28094     }
28095 };
28096
28097 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28098     /**
28099      * Creates and adds a new region if it doesn't already exist.
28100      * @param {String} target The target region key (north, south, east, west or center).
28101      * @param {Object} config The regions config object
28102      * @return {BorderLayoutRegion} The new region
28103      */
28104     addRegion : function(target, config){
28105         if(!this.regions[target]){
28106             var r = this.factory.create(target, this, config);
28107             this.bindRegion(target, r);
28108         }
28109         return this.regions[target];
28110     },
28111
28112     // private (kinda)
28113     bindRegion : function(name, r){
28114         this.regions[name] = r;
28115         r.on("visibilitychange", this.layout, this);
28116         r.on("paneladded", this.layout, this);
28117         r.on("panelremoved", this.layout, this);
28118         r.on("invalidated", this.layout, this);
28119         r.on("resized", this.onRegionResized, this);
28120         r.on("collapsed", this.onRegionCollapsed, this);
28121         r.on("expanded", this.onRegionExpanded, this);
28122     },
28123
28124     /**
28125      * Performs a layout update.
28126      */
28127     layout : function(){
28128         if(this.updating) return;
28129         var size = this.getViewSize();
28130         var w = size.width;
28131         var h = size.height;
28132         var centerW = w;
28133         var centerH = h;
28134         var centerY = 0;
28135         var centerX = 0;
28136         //var x = 0, y = 0;
28137
28138         var rs = this.regions;
28139         var north = rs["north"];
28140         var south = rs["south"]; 
28141         var west = rs["west"];
28142         var east = rs["east"];
28143         var center = rs["center"];
28144         //if(this.hideOnLayout){ // not supported anymore
28145             //c.el.setStyle("display", "none");
28146         //}
28147         if(north && north.isVisible()){
28148             var b = north.getBox();
28149             var m = north.getMargins();
28150             b.width = w - (m.left+m.right);
28151             b.x = m.left;
28152             b.y = m.top;
28153             centerY = b.height + b.y + m.bottom;
28154             centerH -= centerY;
28155             north.updateBox(this.safeBox(b));
28156         }
28157         if(south && south.isVisible()){
28158             var b = south.getBox();
28159             var m = south.getMargins();
28160             b.width = w - (m.left+m.right);
28161             b.x = m.left;
28162             var totalHeight = (b.height + m.top + m.bottom);
28163             b.y = h - totalHeight + m.top;
28164             centerH -= totalHeight;
28165             south.updateBox(this.safeBox(b));
28166         }
28167         if(west && west.isVisible()){
28168             var b = west.getBox();
28169             var m = west.getMargins();
28170             b.height = centerH - (m.top+m.bottom);
28171             b.x = m.left;
28172             b.y = centerY + m.top;
28173             var totalWidth = (b.width + m.left + m.right);
28174             centerX += totalWidth;
28175             centerW -= totalWidth;
28176             west.updateBox(this.safeBox(b));
28177         }
28178         if(east && east.isVisible()){
28179             var b = east.getBox();
28180             var m = east.getMargins();
28181             b.height = centerH - (m.top+m.bottom);
28182             var totalWidth = (b.width + m.left + m.right);
28183             b.x = w - totalWidth + m.left;
28184             b.y = centerY + m.top;
28185             centerW -= totalWidth;
28186             east.updateBox(this.safeBox(b));
28187         }
28188         if(center){
28189             var m = center.getMargins();
28190             var centerBox = {
28191                 x: centerX + m.left,
28192                 y: centerY + m.top,
28193                 width: centerW - (m.left+m.right),
28194                 height: centerH - (m.top+m.bottom)
28195             };
28196             //if(this.hideOnLayout){
28197                 //center.el.setStyle("display", "block");
28198             //}
28199             center.updateBox(this.safeBox(centerBox));
28200         }
28201         this.el.repaint();
28202         this.fireEvent("layout", this);
28203     },
28204
28205     // private
28206     safeBox : function(box){
28207         box.width = Math.max(0, box.width);
28208         box.height = Math.max(0, box.height);
28209         return box;
28210     },
28211
28212     /**
28213      * Adds a ContentPanel (or subclass) to this layout.
28214      * @param {String} target The target region key (north, south, east, west or center).
28215      * @param {Roo.ContentPanel} panel The panel to add
28216      * @return {Roo.ContentPanel} The added panel
28217      */
28218     add : function(target, panel){
28219          
28220         target = target.toLowerCase();
28221         return this.regions[target].add(panel);
28222     },
28223
28224     /**
28225      * Remove a ContentPanel (or subclass) to this layout.
28226      * @param {String} target The target region key (north, south, east, west or center).
28227      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28228      * @return {Roo.ContentPanel} The removed panel
28229      */
28230     remove : function(target, panel){
28231         target = target.toLowerCase();
28232         return this.regions[target].remove(panel);
28233     },
28234
28235     /**
28236      * Searches all regions for a panel with the specified id
28237      * @param {String} panelId
28238      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28239      */
28240     findPanel : function(panelId){
28241         var rs = this.regions;
28242         for(var target in rs){
28243             if(typeof rs[target] != "function"){
28244                 var p = rs[target].getPanel(panelId);
28245                 if(p){
28246                     return p;
28247                 }
28248             }
28249         }
28250         return null;
28251     },
28252
28253     /**
28254      * Searches all regions for a panel with the specified id and activates (shows) it.
28255      * @param {String/ContentPanel} panelId The panels id or the panel itself
28256      * @return {Roo.ContentPanel} The shown panel or null
28257      */
28258     showPanel : function(panelId) {
28259       var rs = this.regions;
28260       for(var target in rs){
28261          var r = rs[target];
28262          if(typeof r != "function"){
28263             if(r.hasPanel(panelId)){
28264                return r.showPanel(panelId);
28265             }
28266          }
28267       }
28268       return null;
28269    },
28270
28271    /**
28272      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28273      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28274      */
28275     restoreState : function(provider){
28276         if(!provider){
28277             provider = Roo.state.Manager;
28278         }
28279         var sm = new Roo.LayoutStateManager();
28280         sm.init(this, provider);
28281     },
28282
28283     /**
28284      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28285      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28286      * a valid ContentPanel config object.  Example:
28287      * <pre><code>
28288 // Create the main layout
28289 var layout = new Roo.BorderLayout('main-ct', {
28290     west: {
28291         split:true,
28292         minSize: 175,
28293         titlebar: true
28294     },
28295     center: {
28296         title:'Components'
28297     }
28298 }, 'main-ct');
28299
28300 // Create and add multiple ContentPanels at once via configs
28301 layout.batchAdd({
28302    west: {
28303        id: 'source-files',
28304        autoCreate:true,
28305        title:'Ext Source Files',
28306        autoScroll:true,
28307        fitToFrame:true
28308    },
28309    center : {
28310        el: cview,
28311        autoScroll:true,
28312        fitToFrame:true,
28313        toolbar: tb,
28314        resizeEl:'cbody'
28315    }
28316 });
28317 </code></pre>
28318      * @param {Object} regions An object containing ContentPanel configs by region name
28319      */
28320     batchAdd : function(regions){
28321         this.beginUpdate();
28322         for(var rname in regions){
28323             var lr = this.regions[rname];
28324             if(lr){
28325                 this.addTypedPanels(lr, regions[rname]);
28326             }
28327         }
28328         this.endUpdate();
28329     },
28330
28331     // private
28332     addTypedPanels : function(lr, ps){
28333         if(typeof ps == 'string'){
28334             lr.add(new Roo.ContentPanel(ps));
28335         }
28336         else if(ps instanceof Array){
28337             for(var i =0, len = ps.length; i < len; i++){
28338                 this.addTypedPanels(lr, ps[i]);
28339             }
28340         }
28341         else if(!ps.events){ // raw config?
28342             var el = ps.el;
28343             delete ps.el; // prevent conflict
28344             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28345         }
28346         else {  // panel object assumed!
28347             lr.add(ps);
28348         }
28349     },
28350     /**
28351      * Adds a xtype elements to the layout.
28352      * <pre><code>
28353
28354 layout.addxtype({
28355        xtype : 'ContentPanel',
28356        region: 'west',
28357        items: [ .... ]
28358    }
28359 );
28360
28361 layout.addxtype({
28362         xtype : 'NestedLayoutPanel',
28363         region: 'west',
28364         layout: {
28365            center: { },
28366            west: { }   
28367         },
28368         items : [ ... list of content panels or nested layout panels.. ]
28369    }
28370 );
28371 </code></pre>
28372      * @param {Object} cfg Xtype definition of item to add.
28373      */
28374     addxtype : function(cfg)
28375     {
28376         // basically accepts a pannel...
28377         // can accept a layout region..!?!?
28378        // console.log('BorderLayout add ' + cfg.xtype)
28379         
28380         if (!cfg.xtype.match(/Panel$/)) {
28381             return false;
28382         }
28383         var ret = false;
28384         var region = cfg.region;
28385         delete cfg.region;
28386         
28387           
28388         var xitems = [];
28389         if (cfg.items) {
28390             xitems = cfg.items;
28391             delete cfg.items;
28392         }
28393         
28394         
28395         switch(cfg.xtype) 
28396         {
28397             case 'ContentPanel':  // ContentPanel (el, cfg)
28398                 if(cfg.autoCreate) {
28399                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28400                 } else {
28401                     var el = this.el.createChild();
28402                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28403                 }
28404                 
28405                 this.add(region, ret);
28406                 break;
28407             
28408             
28409             case 'TreePanel': // our new panel!
28410                 cfg.el = this.el.createChild();
28411                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28412                 this.add(region, ret);
28413                 break;
28414             
28415             case 'NestedLayoutPanel': 
28416                 // create a new Layout (which is  a Border Layout...
28417                 var el = this.el.createChild();
28418                 var clayout = cfg.layout;
28419                 delete cfg.layout;
28420                 clayout.items   = clayout.items  || [];
28421                 // replace this exitems with the clayout ones..
28422                 xitems = clayout.items;
28423                  
28424                 
28425                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
28426                     cfg.background = false;
28427                 }
28428                 var layout = new Roo.BorderLayout(el, clayout);
28429                 
28430                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
28431                 //console.log('adding nested layout panel '  + cfg.toSource());
28432                 this.add(region, ret);
28433                 
28434                 break;
28435                 
28436             case 'GridPanel': 
28437             
28438                 // needs grid and region
28439                 
28440                 //var el = this.getRegion(region).el.createChild();
28441                 var el = this.el.createChild();
28442                 // create the grid first...
28443                 
28444                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
28445                 delete cfg.grid;
28446                 if (region == 'center' && this.active ) {
28447                     cfg.background = false;
28448                 }
28449                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
28450                 
28451                 this.add(region, ret);
28452                 if (cfg.background) {
28453                     ret.on('activate', function(gp) {
28454                         if (!gp.grid.rendered) {
28455                             gp.grid.render();
28456                         }
28457                     });
28458                 } else {
28459                     grid.render();
28460                 }
28461                 break;
28462            
28463                
28464                 
28465                 
28466             default: 
28467                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
28468                 return;
28469              // GridPanel (grid, cfg)
28470             
28471         }
28472         this.beginUpdate();
28473         // add children..
28474         Roo.each(xitems, function(i)  {
28475             ret.addxtype(i);
28476         });
28477         this.endUpdate();
28478         return ret;
28479         
28480     }
28481 });
28482
28483 /**
28484  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
28485  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
28486  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
28487  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
28488  * <pre><code>
28489 // shorthand
28490 var CP = Roo.ContentPanel;
28491
28492 var layout = Roo.BorderLayout.create({
28493     north: {
28494         initialSize: 25,
28495         titlebar: false,
28496         panels: [new CP("north", "North")]
28497     },
28498     west: {
28499         split:true,
28500         initialSize: 200,
28501         minSize: 175,
28502         maxSize: 400,
28503         titlebar: true,
28504         collapsible: true,
28505         panels: [new CP("west", {title: "West"})]
28506     },
28507     east: {
28508         split:true,
28509         initialSize: 202,
28510         minSize: 175,
28511         maxSize: 400,
28512         titlebar: true,
28513         collapsible: true,
28514         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
28515     },
28516     south: {
28517         split:true,
28518         initialSize: 100,
28519         minSize: 100,
28520         maxSize: 200,
28521         titlebar: true,
28522         collapsible: true,
28523         panels: [new CP("south", {title: "South", closable: true})]
28524     },
28525     center: {
28526         titlebar: true,
28527         autoScroll:true,
28528         resizeTabs: true,
28529         minTabWidth: 50,
28530         preferredTabWidth: 150,
28531         panels: [
28532             new CP("center1", {title: "Close Me", closable: true}),
28533             new CP("center2", {title: "Center Panel", closable: false})
28534         ]
28535     }
28536 }, document.body);
28537
28538 layout.getRegion("center").showPanel("center1");
28539 </code></pre>
28540  * @param config
28541  * @param targetEl
28542  */
28543 Roo.BorderLayout.create = function(config, targetEl){
28544     var layout = new Roo.BorderLayout(targetEl || document.body, config);
28545     layout.beginUpdate();
28546     var regions = Roo.BorderLayout.RegionFactory.validRegions;
28547     for(var j = 0, jlen = regions.length; j < jlen; j++){
28548         var lr = regions[j];
28549         if(layout.regions[lr] && config[lr].panels){
28550             var r = layout.regions[lr];
28551             var ps = config[lr].panels;
28552             layout.addTypedPanels(r, ps);
28553         }
28554     }
28555     layout.endUpdate();
28556     return layout;
28557 };
28558
28559 // private
28560 Roo.BorderLayout.RegionFactory = {
28561     // private
28562     validRegions : ["north","south","east","west","center"],
28563
28564     // private
28565     create : function(target, mgr, config){
28566         target = target.toLowerCase();
28567         if(config.lightweight || config.basic){
28568             return new Roo.BasicLayoutRegion(mgr, config, target);
28569         }
28570         switch(target){
28571             case "north":
28572                 return new Roo.NorthLayoutRegion(mgr, config);
28573             case "south":
28574                 return new Roo.SouthLayoutRegion(mgr, config);
28575             case "east":
28576                 return new Roo.EastLayoutRegion(mgr, config);
28577             case "west":
28578                 return new Roo.WestLayoutRegion(mgr, config);
28579             case "center":
28580                 return new Roo.CenterLayoutRegion(mgr, config);
28581         }
28582         throw 'Layout region "'+target+'" not supported.';
28583     }
28584 };/*
28585  * Based on:
28586  * Ext JS Library 1.1.1
28587  * Copyright(c) 2006-2007, Ext JS, LLC.
28588  *
28589  * Originally Released Under LGPL - original licence link has changed is not relivant.
28590  *
28591  * Fork - LGPL
28592  * <script type="text/javascript">
28593  */
28594  
28595 /**
28596  * @class Roo.BasicLayoutRegion
28597  * @extends Roo.util.Observable
28598  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
28599  * and does not have a titlebar, tabs or any other features. All it does is size and position 
28600  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
28601  */
28602 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
28603     this.mgr = mgr;
28604     this.position  = pos;
28605     this.events = {
28606         /**
28607          * @scope Roo.BasicLayoutRegion
28608          */
28609         
28610         /**
28611          * @event beforeremove
28612          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
28613          * @param {Roo.LayoutRegion} this
28614          * @param {Roo.ContentPanel} panel The panel
28615          * @param {Object} e The cancel event object
28616          */
28617         "beforeremove" : true,
28618         /**
28619          * @event invalidated
28620          * Fires when the layout for this region is changed.
28621          * @param {Roo.LayoutRegion} this
28622          */
28623         "invalidated" : true,
28624         /**
28625          * @event visibilitychange
28626          * Fires when this region is shown or hidden 
28627          * @param {Roo.LayoutRegion} this
28628          * @param {Boolean} visibility true or false
28629          */
28630         "visibilitychange" : true,
28631         /**
28632          * @event paneladded
28633          * Fires when a panel is added. 
28634          * @param {Roo.LayoutRegion} this
28635          * @param {Roo.ContentPanel} panel The panel
28636          */
28637         "paneladded" : true,
28638         /**
28639          * @event panelremoved
28640          * Fires when a panel is removed. 
28641          * @param {Roo.LayoutRegion} this
28642          * @param {Roo.ContentPanel} panel The panel
28643          */
28644         "panelremoved" : true,
28645         /**
28646          * @event collapsed
28647          * Fires when this region is collapsed.
28648          * @param {Roo.LayoutRegion} this
28649          */
28650         "collapsed" : true,
28651         /**
28652          * @event expanded
28653          * Fires when this region is expanded.
28654          * @param {Roo.LayoutRegion} this
28655          */
28656         "expanded" : true,
28657         /**
28658          * @event slideshow
28659          * Fires when this region is slid into view.
28660          * @param {Roo.LayoutRegion} this
28661          */
28662         "slideshow" : true,
28663         /**
28664          * @event slidehide
28665          * Fires when this region slides out of view. 
28666          * @param {Roo.LayoutRegion} this
28667          */
28668         "slidehide" : true,
28669         /**
28670          * @event panelactivated
28671          * Fires when a panel is activated. 
28672          * @param {Roo.LayoutRegion} this
28673          * @param {Roo.ContentPanel} panel The activated panel
28674          */
28675         "panelactivated" : true,
28676         /**
28677          * @event resized
28678          * Fires when the user resizes this region. 
28679          * @param {Roo.LayoutRegion} this
28680          * @param {Number} newSize The new size (width for east/west, height for north/south)
28681          */
28682         "resized" : true
28683     };
28684     /** A collection of panels in this region. @type Roo.util.MixedCollection */
28685     this.panels = new Roo.util.MixedCollection();
28686     this.panels.getKey = this.getPanelId.createDelegate(this);
28687     this.box = null;
28688     this.activePanel = null;
28689     // ensure listeners are added...
28690     
28691     if (config.listeners || config.events) {
28692         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
28693             listeners : config.listeners || {},
28694             events : config.events || {}
28695         });
28696     }
28697     
28698     if(skipConfig !== true){
28699         this.applyConfig(config);
28700     }
28701 };
28702
28703 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
28704     getPanelId : function(p){
28705         return p.getId();
28706     },
28707     
28708     applyConfig : function(config){
28709         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
28710         this.config = config;
28711         
28712     },
28713     
28714     /**
28715      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
28716      * the width, for horizontal (north, south) the height.
28717      * @param {Number} newSize The new width or height
28718      */
28719     resizeTo : function(newSize){
28720         var el = this.el ? this.el :
28721                  (this.activePanel ? this.activePanel.getEl() : null);
28722         if(el){
28723             switch(this.position){
28724                 case "east":
28725                 case "west":
28726                     el.setWidth(newSize);
28727                     this.fireEvent("resized", this, newSize);
28728                 break;
28729                 case "north":
28730                 case "south":
28731                     el.setHeight(newSize);
28732                     this.fireEvent("resized", this, newSize);
28733                 break;                
28734             }
28735         }
28736     },
28737     
28738     getBox : function(){
28739         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
28740     },
28741     
28742     getMargins : function(){
28743         return this.margins;
28744     },
28745     
28746     updateBox : function(box){
28747         this.box = box;
28748         var el = this.activePanel.getEl();
28749         el.dom.style.left = box.x + "px";
28750         el.dom.style.top = box.y + "px";
28751         this.activePanel.setSize(box.width, box.height);
28752     },
28753     
28754     /**
28755      * Returns the container element for this region.
28756      * @return {Roo.Element}
28757      */
28758     getEl : function(){
28759         return this.activePanel;
28760     },
28761     
28762     /**
28763      * Returns true if this region is currently visible.
28764      * @return {Boolean}
28765      */
28766     isVisible : function(){
28767         return this.activePanel ? true : false;
28768     },
28769     
28770     setActivePanel : function(panel){
28771         panel = this.getPanel(panel);
28772         if(this.activePanel && this.activePanel != panel){
28773             this.activePanel.setActiveState(false);
28774             this.activePanel.getEl().setLeftTop(-10000,-10000);
28775         }
28776         this.activePanel = panel;
28777         panel.setActiveState(true);
28778         if(this.box){
28779             panel.setSize(this.box.width, this.box.height);
28780         }
28781         this.fireEvent("panelactivated", this, panel);
28782         this.fireEvent("invalidated");
28783     },
28784     
28785     /**
28786      * Show the specified panel.
28787      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
28788      * @return {Roo.ContentPanel} The shown panel or null
28789      */
28790     showPanel : function(panel){
28791         if(panel = this.getPanel(panel)){
28792             this.setActivePanel(panel);
28793         }
28794         return panel;
28795     },
28796     
28797     /**
28798      * Get the active panel for this region.
28799      * @return {Roo.ContentPanel} The active panel or null
28800      */
28801     getActivePanel : function(){
28802         return this.activePanel;
28803     },
28804     
28805     /**
28806      * Add the passed ContentPanel(s)
28807      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
28808      * @return {Roo.ContentPanel} The panel added (if only one was added)
28809      */
28810     add : function(panel){
28811         if(arguments.length > 1){
28812             for(var i = 0, len = arguments.length; i < len; i++) {
28813                 this.add(arguments[i]);
28814             }
28815             return null;
28816         }
28817         if(this.hasPanel(panel)){
28818             this.showPanel(panel);
28819             return panel;
28820         }
28821         var el = panel.getEl();
28822         if(el.dom.parentNode != this.mgr.el.dom){
28823             this.mgr.el.dom.appendChild(el.dom);
28824         }
28825         if(panel.setRegion){
28826             panel.setRegion(this);
28827         }
28828         this.panels.add(panel);
28829         el.setStyle("position", "absolute");
28830         if(!panel.background){
28831             this.setActivePanel(panel);
28832             if(this.config.initialSize && this.panels.getCount()==1){
28833                 this.resizeTo(this.config.initialSize);
28834             }
28835         }
28836         this.fireEvent("paneladded", this, panel);
28837         return panel;
28838     },
28839     
28840     /**
28841      * Returns true if the panel is in this region.
28842      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28843      * @return {Boolean}
28844      */
28845     hasPanel : function(panel){
28846         if(typeof panel == "object"){ // must be panel obj
28847             panel = panel.getId();
28848         }
28849         return this.getPanel(panel) ? true : false;
28850     },
28851     
28852     /**
28853      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
28854      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28855      * @param {Boolean} preservePanel Overrides the config preservePanel option
28856      * @return {Roo.ContentPanel} The panel that was removed
28857      */
28858     remove : function(panel, preservePanel){
28859         panel = this.getPanel(panel);
28860         if(!panel){
28861             return null;
28862         }
28863         var e = {};
28864         this.fireEvent("beforeremove", this, panel, e);
28865         if(e.cancel === true){
28866             return null;
28867         }
28868         var panelId = panel.getId();
28869         this.panels.removeKey(panelId);
28870         return panel;
28871     },
28872     
28873     /**
28874      * Returns the panel specified or null if it's not in this region.
28875      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28876      * @return {Roo.ContentPanel}
28877      */
28878     getPanel : function(id){
28879         if(typeof id == "object"){ // must be panel obj
28880             return id;
28881         }
28882         return this.panels.get(id);
28883     },
28884     
28885     /**
28886      * Returns this regions position (north/south/east/west/center).
28887      * @return {String} 
28888      */
28889     getPosition: function(){
28890         return this.position;    
28891     }
28892 });/*
28893  * Based on:
28894  * Ext JS Library 1.1.1
28895  * Copyright(c) 2006-2007, Ext JS, LLC.
28896  *
28897  * Originally Released Under LGPL - original licence link has changed is not relivant.
28898  *
28899  * Fork - LGPL
28900  * <script type="text/javascript">
28901  */
28902  
28903 /**
28904  * @class Roo.LayoutRegion
28905  * @extends Roo.BasicLayoutRegion
28906  * This class represents a region in a layout manager.
28907  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
28908  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
28909  * @cfg {Boolean} floatable False to disable floating (defaults to true)
28910  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
28911  * @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})
28912  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
28913  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
28914  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
28915  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
28916  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
28917  * @cfg {String} title The title for the region (overrides panel titles)
28918  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
28919  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
28920  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
28921  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
28922  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
28923  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
28924  * the space available, similar to FireFox 1.5 tabs (defaults to false)
28925  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
28926  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
28927  * @cfg {Boolean} showPin True to show a pin button
28928 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
28929 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
28930 * @cfg {Boolean} disableTabTips True to disable tab tooltips
28931 * @cfg {Number} width  For East/West panels
28932 * @cfg {Number} height For North/South panels
28933 * @cfg {Boolean} split To show the splitter
28934  */
28935 Roo.LayoutRegion = function(mgr, config, pos){
28936     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
28937     var dh = Roo.DomHelper;
28938     /** This region's container element 
28939     * @type Roo.Element */
28940     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
28941     /** This region's title element 
28942     * @type Roo.Element */
28943
28944     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
28945         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
28946         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
28947     ]}, true);
28948     this.titleEl.enableDisplayMode();
28949     /** This region's title text element 
28950     * @type HTMLElement */
28951     this.titleTextEl = this.titleEl.dom.firstChild;
28952     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
28953     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
28954     this.closeBtn.enableDisplayMode();
28955     this.closeBtn.on("click", this.closeClicked, this);
28956     this.closeBtn.hide();
28957
28958     this.createBody(config);
28959     this.visible = true;
28960     this.collapsed = false;
28961
28962     if(config.hideWhenEmpty){
28963         this.hide();
28964         this.on("paneladded", this.validateVisibility, this);
28965         this.on("panelremoved", this.validateVisibility, this);
28966     }
28967     this.applyConfig(config);
28968 };
28969
28970 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
28971
28972     createBody : function(){
28973         /** This region's body element 
28974         * @type Roo.Element */
28975         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
28976     },
28977
28978     applyConfig : function(c){
28979         if(c.collapsible && this.position != "center" && !this.collapsedEl){
28980             var dh = Roo.DomHelper;
28981             if(c.titlebar !== false){
28982                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
28983                 this.collapseBtn.on("click", this.collapse, this);
28984                 this.collapseBtn.enableDisplayMode();
28985
28986                 if(c.showPin === true || this.showPin){
28987                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
28988                     this.stickBtn.enableDisplayMode();
28989                     this.stickBtn.on("click", this.expand, this);
28990                     this.stickBtn.hide();
28991                 }
28992             }
28993             /** This region's collapsed element
28994             * @type Roo.Element */
28995             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
28996                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
28997             ]}, true);
28998             if(c.floatable !== false){
28999                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29000                this.collapsedEl.on("click", this.collapseClick, this);
29001             }
29002
29003             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29004                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29005                    id: "message", unselectable: "on", style:{"float":"left"}});
29006                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29007              }
29008             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29009             this.expandBtn.on("click", this.expand, this);
29010         }
29011         if(this.collapseBtn){
29012             this.collapseBtn.setVisible(c.collapsible == true);
29013         }
29014         this.cmargins = c.cmargins || this.cmargins ||
29015                          (this.position == "west" || this.position == "east" ?
29016                              {top: 0, left: 2, right:2, bottom: 0} :
29017                              {top: 2, left: 0, right:0, bottom: 2});
29018         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29019         this.bottomTabs = c.tabPosition != "top";
29020         this.autoScroll = c.autoScroll || false;
29021         if(this.autoScroll){
29022             this.bodyEl.setStyle("overflow", "auto");
29023         }else{
29024             this.bodyEl.setStyle("overflow", "hidden");
29025         }
29026         //if(c.titlebar !== false){
29027             if((!c.titlebar && !c.title) || c.titlebar === false){
29028                 this.titleEl.hide();
29029             }else{
29030                 this.titleEl.show();
29031                 if(c.title){
29032                     this.titleTextEl.innerHTML = c.title;
29033                 }
29034             }
29035         //}
29036         this.duration = c.duration || .30;
29037         this.slideDuration = c.slideDuration || .45;
29038         this.config = c;
29039         if(c.collapsed){
29040             this.collapse(true);
29041         }
29042         if(c.hidden){
29043             this.hide();
29044         }
29045     },
29046     /**
29047      * Returns true if this region is currently visible.
29048      * @return {Boolean}
29049      */
29050     isVisible : function(){
29051         return this.visible;
29052     },
29053
29054     /**
29055      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29056      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29057      */
29058     setCollapsedTitle : function(title){
29059         title = title || "&#160;";
29060         if(this.collapsedTitleTextEl){
29061             this.collapsedTitleTextEl.innerHTML = title;
29062         }
29063     },
29064
29065     getBox : function(){
29066         var b;
29067         if(!this.collapsed){
29068             b = this.el.getBox(false, true);
29069         }else{
29070             b = this.collapsedEl.getBox(false, true);
29071         }
29072         return b;
29073     },
29074
29075     getMargins : function(){
29076         return this.collapsed ? this.cmargins : this.margins;
29077     },
29078
29079     highlight : function(){
29080         this.el.addClass("x-layout-panel-dragover");
29081     },
29082
29083     unhighlight : function(){
29084         this.el.removeClass("x-layout-panel-dragover");
29085     },
29086
29087     updateBox : function(box){
29088         this.box = box;
29089         if(!this.collapsed){
29090             this.el.dom.style.left = box.x + "px";
29091             this.el.dom.style.top = box.y + "px";
29092             this.updateBody(box.width, box.height);
29093         }else{
29094             this.collapsedEl.dom.style.left = box.x + "px";
29095             this.collapsedEl.dom.style.top = box.y + "px";
29096             this.collapsedEl.setSize(box.width, box.height);
29097         }
29098         if(this.tabs){
29099             this.tabs.autoSizeTabs();
29100         }
29101     },
29102
29103     updateBody : function(w, h){
29104         if(w !== null){
29105             this.el.setWidth(w);
29106             w -= this.el.getBorderWidth("rl");
29107             if(this.config.adjustments){
29108                 w += this.config.adjustments[0];
29109             }
29110         }
29111         if(h !== null){
29112             this.el.setHeight(h);
29113             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29114             h -= this.el.getBorderWidth("tb");
29115             if(this.config.adjustments){
29116                 h += this.config.adjustments[1];
29117             }
29118             this.bodyEl.setHeight(h);
29119             if(this.tabs){
29120                 h = this.tabs.syncHeight(h);
29121             }
29122         }
29123         if(this.panelSize){
29124             w = w !== null ? w : this.panelSize.width;
29125             h = h !== null ? h : this.panelSize.height;
29126         }
29127         if(this.activePanel){
29128             var el = this.activePanel.getEl();
29129             w = w !== null ? w : el.getWidth();
29130             h = h !== null ? h : el.getHeight();
29131             this.panelSize = {width: w, height: h};
29132             this.activePanel.setSize(w, h);
29133         }
29134         if(Roo.isIE && this.tabs){
29135             this.tabs.el.repaint();
29136         }
29137     },
29138
29139     /**
29140      * Returns the container element for this region.
29141      * @return {Roo.Element}
29142      */
29143     getEl : function(){
29144         return this.el;
29145     },
29146
29147     /**
29148      * Hides this region.
29149      */
29150     hide : function(){
29151         if(!this.collapsed){
29152             this.el.dom.style.left = "-2000px";
29153             this.el.hide();
29154         }else{
29155             this.collapsedEl.dom.style.left = "-2000px";
29156             this.collapsedEl.hide();
29157         }
29158         this.visible = false;
29159         this.fireEvent("visibilitychange", this, false);
29160     },
29161
29162     /**
29163      * Shows this region if it was previously hidden.
29164      */
29165     show : function(){
29166         if(!this.collapsed){
29167             this.el.show();
29168         }else{
29169             this.collapsedEl.show();
29170         }
29171         this.visible = true;
29172         this.fireEvent("visibilitychange", this, true);
29173     },
29174
29175     closeClicked : function(){
29176         if(this.activePanel){
29177             this.remove(this.activePanel);
29178         }
29179     },
29180
29181     collapseClick : function(e){
29182         if(this.isSlid){
29183            e.stopPropagation();
29184            this.slideIn();
29185         }else{
29186            e.stopPropagation();
29187            this.slideOut();
29188         }
29189     },
29190
29191     /**
29192      * Collapses this region.
29193      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29194      */
29195     collapse : function(skipAnim){
29196         if(this.collapsed) return;
29197         this.collapsed = true;
29198         if(this.split){
29199             this.split.el.hide();
29200         }
29201         if(this.config.animate && skipAnim !== true){
29202             this.fireEvent("invalidated", this);
29203             this.animateCollapse();
29204         }else{
29205             this.el.setLocation(-20000,-20000);
29206             this.el.hide();
29207             this.collapsedEl.show();
29208             this.fireEvent("collapsed", this);
29209             this.fireEvent("invalidated", this);
29210         }
29211     },
29212
29213     animateCollapse : function(){
29214         // overridden
29215     },
29216
29217     /**
29218      * Expands this region if it was previously collapsed.
29219      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29220      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29221      */
29222     expand : function(e, skipAnim){
29223         if(e) e.stopPropagation();
29224         if(!this.collapsed || this.el.hasActiveFx()) return;
29225         if(this.isSlid){
29226             this.afterSlideIn();
29227             skipAnim = true;
29228         }
29229         this.collapsed = false;
29230         if(this.config.animate && skipAnim !== true){
29231             this.animateExpand();
29232         }else{
29233             this.el.show();
29234             if(this.split){
29235                 this.split.el.show();
29236             }
29237             this.collapsedEl.setLocation(-2000,-2000);
29238             this.collapsedEl.hide();
29239             this.fireEvent("invalidated", this);
29240             this.fireEvent("expanded", this);
29241         }
29242     },
29243
29244     animateExpand : function(){
29245         // overridden
29246     },
29247
29248     initTabs : function(){
29249         this.bodyEl.setStyle("overflow", "hidden");
29250         var ts = new Roo.TabPanel(this.bodyEl.dom, {
29251             tabPosition: this.bottomTabs ? 'bottom' : 'top',
29252             disableTooltips: this.config.disableTabTips
29253         });
29254         if(this.config.hideTabs){
29255             ts.stripWrap.setDisplayed(false);
29256         }
29257         this.tabs = ts;
29258         ts.resizeTabs = this.config.resizeTabs === true;
29259         ts.minTabWidth = this.config.minTabWidth || 40;
29260         ts.maxTabWidth = this.config.maxTabWidth || 250;
29261         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29262         ts.monitorResize = false;
29263         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29264         ts.bodyEl.addClass('x-layout-tabs-body');
29265         this.panels.each(this.initPanelAsTab, this);
29266     },
29267
29268     initPanelAsTab : function(panel){
29269         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29270                     this.config.closeOnTab && panel.isClosable());
29271         if(panel.tabTip !== undefined){
29272             ti.setTooltip(panel.tabTip);
29273         }
29274         ti.on("activate", function(){
29275               this.setActivePanel(panel);
29276         }, this);
29277         if(this.config.closeOnTab){
29278             ti.on("beforeclose", function(t, e){
29279                 e.cancel = true;
29280                 this.remove(panel);
29281             }, this);
29282         }
29283         return ti;
29284     },
29285
29286     updatePanelTitle : function(panel, title){
29287         if(this.activePanel == panel){
29288             this.updateTitle(title);
29289         }
29290         if(this.tabs){
29291             var ti = this.tabs.getTab(panel.getEl().id);
29292             ti.setText(title);
29293             if(panel.tabTip !== undefined){
29294                 ti.setTooltip(panel.tabTip);
29295             }
29296         }
29297     },
29298
29299     updateTitle : function(title){
29300         if(this.titleTextEl && !this.config.title){
29301             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29302         }
29303     },
29304
29305     setActivePanel : function(panel){
29306         panel = this.getPanel(panel);
29307         if(this.activePanel && this.activePanel != panel){
29308             this.activePanel.setActiveState(false);
29309         }
29310         this.activePanel = panel;
29311         panel.setActiveState(true);
29312         if(this.panelSize){
29313             panel.setSize(this.panelSize.width, this.panelSize.height);
29314         }
29315         if(this.closeBtn){
29316             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29317         }
29318         this.updateTitle(panel.getTitle());
29319         if(this.tabs){
29320             this.fireEvent("invalidated", this);
29321         }
29322         this.fireEvent("panelactivated", this, panel);
29323     },
29324
29325     /**
29326      * Shows the specified panel.
29327      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29328      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29329      */
29330     showPanel : function(panel){
29331         if(panel = this.getPanel(panel)){
29332             if(this.tabs){
29333                 var tab = this.tabs.getTab(panel.getEl().id);
29334                 if(tab.isHidden()){
29335                     this.tabs.unhideTab(tab.id);
29336                 }
29337                 tab.activate();
29338             }else{
29339                 this.setActivePanel(panel);
29340             }
29341         }
29342         return panel;
29343     },
29344
29345     /**
29346      * Get the active panel for this region.
29347      * @return {Roo.ContentPanel} The active panel or null
29348      */
29349     getActivePanel : function(){
29350         return this.activePanel;
29351     },
29352
29353     validateVisibility : function(){
29354         if(this.panels.getCount() < 1){
29355             this.updateTitle("&#160;");
29356             this.closeBtn.hide();
29357             this.hide();
29358         }else{
29359             if(!this.isVisible()){
29360                 this.show();
29361             }
29362         }
29363     },
29364
29365     /**
29366      * Adds the passed ContentPanel(s) to this region.
29367      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29368      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
29369      */
29370     add : function(panel){
29371         if(arguments.length > 1){
29372             for(var i = 0, len = arguments.length; i < len; i++) {
29373                 this.add(arguments[i]);
29374             }
29375             return null;
29376         }
29377         if(this.hasPanel(panel)){
29378             this.showPanel(panel);
29379             return panel;
29380         }
29381         panel.setRegion(this);
29382         this.panels.add(panel);
29383         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
29384             this.bodyEl.dom.appendChild(panel.getEl().dom);
29385             if(panel.background !== true){
29386                 this.setActivePanel(panel);
29387             }
29388             this.fireEvent("paneladded", this, panel);
29389             return panel;
29390         }
29391         if(!this.tabs){
29392             this.initTabs();
29393         }else{
29394             this.initPanelAsTab(panel);
29395         }
29396         if(panel.background !== true){
29397             this.tabs.activate(panel.getEl().id);
29398         }
29399         this.fireEvent("paneladded", this, panel);
29400         return panel;
29401     },
29402
29403     /**
29404      * Hides the tab for the specified panel.
29405      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29406      */
29407     hidePanel : function(panel){
29408         if(this.tabs && (panel = this.getPanel(panel))){
29409             this.tabs.hideTab(panel.getEl().id);
29410         }
29411     },
29412
29413     /**
29414      * Unhides the tab for a previously hidden panel.
29415      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29416      */
29417     unhidePanel : function(panel){
29418         if(this.tabs && (panel = this.getPanel(panel))){
29419             this.tabs.unhideTab(panel.getEl().id);
29420         }
29421     },
29422
29423     clearPanels : function(){
29424         while(this.panels.getCount() > 0){
29425              this.remove(this.panels.first());
29426         }
29427     },
29428
29429     /**
29430      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29431      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29432      * @param {Boolean} preservePanel Overrides the config preservePanel option
29433      * @return {Roo.ContentPanel} The panel that was removed
29434      */
29435     remove : function(panel, preservePanel){
29436         panel = this.getPanel(panel);
29437         if(!panel){
29438             return null;
29439         }
29440         var e = {};
29441         this.fireEvent("beforeremove", this, panel, e);
29442         if(e.cancel === true){
29443             return null;
29444         }
29445         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
29446         var panelId = panel.getId();
29447         this.panels.removeKey(panelId);
29448         if(preservePanel){
29449             document.body.appendChild(panel.getEl().dom);
29450         }
29451         if(this.tabs){
29452             this.tabs.removeTab(panel.getEl().id);
29453         }else if (!preservePanel){
29454             this.bodyEl.dom.removeChild(panel.getEl().dom);
29455         }
29456         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
29457             var p = this.panels.first();
29458             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
29459             tempEl.appendChild(p.getEl().dom);
29460             this.bodyEl.update("");
29461             this.bodyEl.dom.appendChild(p.getEl().dom);
29462             tempEl = null;
29463             this.updateTitle(p.getTitle());
29464             this.tabs = null;
29465             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29466             this.setActivePanel(p);
29467         }
29468         panel.setRegion(null);
29469         if(this.activePanel == panel){
29470             this.activePanel = null;
29471         }
29472         if(this.config.autoDestroy !== false && preservePanel !== true){
29473             try{panel.destroy();}catch(e){}
29474         }
29475         this.fireEvent("panelremoved", this, panel);
29476         return panel;
29477     },
29478
29479     /**
29480      * Returns the TabPanel component used by this region
29481      * @return {Roo.TabPanel}
29482      */
29483     getTabs : function(){
29484         return this.tabs;
29485     },
29486
29487     createTool : function(parentEl, className){
29488         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
29489             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
29490         btn.addClassOnOver("x-layout-tools-button-over");
29491         return btn;
29492     }
29493 });/*
29494  * Based on:
29495  * Ext JS Library 1.1.1
29496  * Copyright(c) 2006-2007, Ext JS, LLC.
29497  *
29498  * Originally Released Under LGPL - original licence link has changed is not relivant.
29499  *
29500  * Fork - LGPL
29501  * <script type="text/javascript">
29502  */
29503  
29504
29505
29506 /**
29507  * @class Roo.SplitLayoutRegion
29508  * @extends Roo.LayoutRegion
29509  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
29510  */
29511 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
29512     this.cursor = cursor;
29513     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
29514 };
29515
29516 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
29517     splitTip : "Drag to resize.",
29518     collapsibleSplitTip : "Drag to resize. Double click to hide.",
29519     useSplitTips : false,
29520
29521     applyConfig : function(config){
29522         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
29523         if(config.split){
29524             if(!this.split){
29525                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
29526                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
29527                 /** The SplitBar for this region 
29528                 * @type Roo.SplitBar */
29529                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
29530                 this.split.on("moved", this.onSplitMove, this);
29531                 this.split.useShim = config.useShim === true;
29532                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
29533                 if(this.useSplitTips){
29534                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
29535                 }
29536                 if(config.collapsible){
29537                     this.split.el.on("dblclick", this.collapse,  this);
29538                 }
29539             }
29540             if(typeof config.minSize != "undefined"){
29541                 this.split.minSize = config.minSize;
29542             }
29543             if(typeof config.maxSize != "undefined"){
29544                 this.split.maxSize = config.maxSize;
29545             }
29546             if(config.hideWhenEmpty || config.hidden || config.collapsed){
29547                 this.hideSplitter();
29548             }
29549         }
29550     },
29551
29552     getHMaxSize : function(){
29553          var cmax = this.config.maxSize || 10000;
29554          var center = this.mgr.getRegion("center");
29555          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
29556     },
29557
29558     getVMaxSize : function(){
29559          var cmax = this.config.maxSize || 10000;
29560          var center = this.mgr.getRegion("center");
29561          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
29562     },
29563
29564     onSplitMove : function(split, newSize){
29565         this.fireEvent("resized", this, newSize);
29566     },
29567     
29568     /** 
29569      * Returns the {@link Roo.SplitBar} for this region.
29570      * @return {Roo.SplitBar}
29571      */
29572     getSplitBar : function(){
29573         return this.split;
29574     },
29575     
29576     hide : function(){
29577         this.hideSplitter();
29578         Roo.SplitLayoutRegion.superclass.hide.call(this);
29579     },
29580
29581     hideSplitter : function(){
29582         if(this.split){
29583             this.split.el.setLocation(-2000,-2000);
29584             this.split.el.hide();
29585         }
29586     },
29587
29588     show : function(){
29589         if(this.split){
29590             this.split.el.show();
29591         }
29592         Roo.SplitLayoutRegion.superclass.show.call(this);
29593     },
29594     
29595     beforeSlide: function(){
29596         if(Roo.isGecko){// firefox overflow auto bug workaround
29597             this.bodyEl.clip();
29598             if(this.tabs) this.tabs.bodyEl.clip();
29599             if(this.activePanel){
29600                 this.activePanel.getEl().clip();
29601                 
29602                 if(this.activePanel.beforeSlide){
29603                     this.activePanel.beforeSlide();
29604                 }
29605             }
29606         }
29607     },
29608     
29609     afterSlide : function(){
29610         if(Roo.isGecko){// firefox overflow auto bug workaround
29611             this.bodyEl.unclip();
29612             if(this.tabs) this.tabs.bodyEl.unclip();
29613             if(this.activePanel){
29614                 this.activePanel.getEl().unclip();
29615                 if(this.activePanel.afterSlide){
29616                     this.activePanel.afterSlide();
29617                 }
29618             }
29619         }
29620     },
29621
29622     initAutoHide : function(){
29623         if(this.autoHide !== false){
29624             if(!this.autoHideHd){
29625                 var st = new Roo.util.DelayedTask(this.slideIn, this);
29626                 this.autoHideHd = {
29627                     "mouseout": function(e){
29628                         if(!e.within(this.el, true)){
29629                             st.delay(500);
29630                         }
29631                     },
29632                     "mouseover" : function(e){
29633                         st.cancel();
29634                     },
29635                     scope : this
29636                 };
29637             }
29638             this.el.on(this.autoHideHd);
29639         }
29640     },
29641
29642     clearAutoHide : function(){
29643         if(this.autoHide !== false){
29644             this.el.un("mouseout", this.autoHideHd.mouseout);
29645             this.el.un("mouseover", this.autoHideHd.mouseover);
29646         }
29647     },
29648
29649     clearMonitor : function(){
29650         Roo.get(document).un("click", this.slideInIf, this);
29651     },
29652
29653     // these names are backwards but not changed for compat
29654     slideOut : function(){
29655         if(this.isSlid || this.el.hasActiveFx()){
29656             return;
29657         }
29658         this.isSlid = true;
29659         if(this.collapseBtn){
29660             this.collapseBtn.hide();
29661         }
29662         this.closeBtnState = this.closeBtn.getStyle('display');
29663         this.closeBtn.hide();
29664         if(this.stickBtn){
29665             this.stickBtn.show();
29666         }
29667         this.el.show();
29668         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
29669         this.beforeSlide();
29670         this.el.setStyle("z-index", 10001);
29671         this.el.slideIn(this.getSlideAnchor(), {
29672             callback: function(){
29673                 this.afterSlide();
29674                 this.initAutoHide();
29675                 Roo.get(document).on("click", this.slideInIf, this);
29676                 this.fireEvent("slideshow", this);
29677             },
29678             scope: this,
29679             block: true
29680         });
29681     },
29682
29683     afterSlideIn : function(){
29684         this.clearAutoHide();
29685         this.isSlid = false;
29686         this.clearMonitor();
29687         this.el.setStyle("z-index", "");
29688         if(this.collapseBtn){
29689             this.collapseBtn.show();
29690         }
29691         this.closeBtn.setStyle('display', this.closeBtnState);
29692         if(this.stickBtn){
29693             this.stickBtn.hide();
29694         }
29695         this.fireEvent("slidehide", this);
29696     },
29697
29698     slideIn : function(cb){
29699         if(!this.isSlid || this.el.hasActiveFx()){
29700             Roo.callback(cb);
29701             return;
29702         }
29703         this.isSlid = false;
29704         this.beforeSlide();
29705         this.el.slideOut(this.getSlideAnchor(), {
29706             callback: function(){
29707                 this.el.setLeftTop(-10000, -10000);
29708                 this.afterSlide();
29709                 this.afterSlideIn();
29710                 Roo.callback(cb);
29711             },
29712             scope: this,
29713             block: true
29714         });
29715     },
29716     
29717     slideInIf : function(e){
29718         if(!e.within(this.el)){
29719             this.slideIn();
29720         }
29721     },
29722
29723     animateCollapse : function(){
29724         this.beforeSlide();
29725         this.el.setStyle("z-index", 20000);
29726         var anchor = this.getSlideAnchor();
29727         this.el.slideOut(anchor, {
29728             callback : function(){
29729                 this.el.setStyle("z-index", "");
29730                 this.collapsedEl.slideIn(anchor, {duration:.3});
29731                 this.afterSlide();
29732                 this.el.setLocation(-10000,-10000);
29733                 this.el.hide();
29734                 this.fireEvent("collapsed", this);
29735             },
29736             scope: this,
29737             block: true
29738         });
29739     },
29740
29741     animateExpand : function(){
29742         this.beforeSlide();
29743         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
29744         this.el.setStyle("z-index", 20000);
29745         this.collapsedEl.hide({
29746             duration:.1
29747         });
29748         this.el.slideIn(this.getSlideAnchor(), {
29749             callback : function(){
29750                 this.el.setStyle("z-index", "");
29751                 this.afterSlide();
29752                 if(this.split){
29753                     this.split.el.show();
29754                 }
29755                 this.fireEvent("invalidated", this);
29756                 this.fireEvent("expanded", this);
29757             },
29758             scope: this,
29759             block: true
29760         });
29761     },
29762
29763     anchors : {
29764         "west" : "left",
29765         "east" : "right",
29766         "north" : "top",
29767         "south" : "bottom"
29768     },
29769
29770     sanchors : {
29771         "west" : "l",
29772         "east" : "r",
29773         "north" : "t",
29774         "south" : "b"
29775     },
29776
29777     canchors : {
29778         "west" : "tl-tr",
29779         "east" : "tr-tl",
29780         "north" : "tl-bl",
29781         "south" : "bl-tl"
29782     },
29783
29784     getAnchor : function(){
29785         return this.anchors[this.position];
29786     },
29787
29788     getCollapseAnchor : function(){
29789         return this.canchors[this.position];
29790     },
29791
29792     getSlideAnchor : function(){
29793         return this.sanchors[this.position];
29794     },
29795
29796     getAlignAdj : function(){
29797         var cm = this.cmargins;
29798         switch(this.position){
29799             case "west":
29800                 return [0, 0];
29801             break;
29802             case "east":
29803                 return [0, 0];
29804             break;
29805             case "north":
29806                 return [0, 0];
29807             break;
29808             case "south":
29809                 return [0, 0];
29810             break;
29811         }
29812     },
29813
29814     getExpandAdj : function(){
29815         var c = this.collapsedEl, cm = this.cmargins;
29816         switch(this.position){
29817             case "west":
29818                 return [-(cm.right+c.getWidth()+cm.left), 0];
29819             break;
29820             case "east":
29821                 return [cm.right+c.getWidth()+cm.left, 0];
29822             break;
29823             case "north":
29824                 return [0, -(cm.top+cm.bottom+c.getHeight())];
29825             break;
29826             case "south":
29827                 return [0, cm.top+cm.bottom+c.getHeight()];
29828             break;
29829         }
29830     }
29831 });/*
29832  * Based on:
29833  * Ext JS Library 1.1.1
29834  * Copyright(c) 2006-2007, Ext JS, LLC.
29835  *
29836  * Originally Released Under LGPL - original licence link has changed is not relivant.
29837  *
29838  * Fork - LGPL
29839  * <script type="text/javascript">
29840  */
29841 /*
29842  * These classes are private internal classes
29843  */
29844 Roo.CenterLayoutRegion = function(mgr, config){
29845     Roo.LayoutRegion.call(this, mgr, config, "center");
29846     this.visible = true;
29847     this.minWidth = config.minWidth || 20;
29848     this.minHeight = config.minHeight || 20;
29849 };
29850
29851 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
29852     hide : function(){
29853         // center panel can't be hidden
29854     },
29855     
29856     show : function(){
29857         // center panel can't be hidden
29858     },
29859     
29860     getMinWidth: function(){
29861         return this.minWidth;
29862     },
29863     
29864     getMinHeight: function(){
29865         return this.minHeight;
29866     }
29867 });
29868
29869
29870 Roo.NorthLayoutRegion = function(mgr, config){
29871     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
29872     if(this.split){
29873         this.split.placement = Roo.SplitBar.TOP;
29874         this.split.orientation = Roo.SplitBar.VERTICAL;
29875         this.split.el.addClass("x-layout-split-v");
29876     }
29877     var size = config.initialSize || config.height;
29878     if(typeof size != "undefined"){
29879         this.el.setHeight(size);
29880     }
29881 };
29882 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
29883     orientation: Roo.SplitBar.VERTICAL,
29884     getBox : function(){
29885         if(this.collapsed){
29886             return this.collapsedEl.getBox();
29887         }
29888         var box = this.el.getBox();
29889         if(this.split){
29890             box.height += this.split.el.getHeight();
29891         }
29892         return box;
29893     },
29894     
29895     updateBox : function(box){
29896         if(this.split && !this.collapsed){
29897             box.height -= this.split.el.getHeight();
29898             this.split.el.setLeft(box.x);
29899             this.split.el.setTop(box.y+box.height);
29900             this.split.el.setWidth(box.width);
29901         }
29902         if(this.collapsed){
29903             this.updateBody(box.width, null);
29904         }
29905         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29906     }
29907 });
29908
29909 Roo.SouthLayoutRegion = function(mgr, config){
29910     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
29911     if(this.split){
29912         this.split.placement = Roo.SplitBar.BOTTOM;
29913         this.split.orientation = Roo.SplitBar.VERTICAL;
29914         this.split.el.addClass("x-layout-split-v");
29915     }
29916     var size = config.initialSize || config.height;
29917     if(typeof size != "undefined"){
29918         this.el.setHeight(size);
29919     }
29920 };
29921 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
29922     orientation: Roo.SplitBar.VERTICAL,
29923     getBox : function(){
29924         if(this.collapsed){
29925             return this.collapsedEl.getBox();
29926         }
29927         var box = this.el.getBox();
29928         if(this.split){
29929             var sh = this.split.el.getHeight();
29930             box.height += sh;
29931             box.y -= sh;
29932         }
29933         return box;
29934     },
29935     
29936     updateBox : function(box){
29937         if(this.split && !this.collapsed){
29938             var sh = this.split.el.getHeight();
29939             box.height -= sh;
29940             box.y += sh;
29941             this.split.el.setLeft(box.x);
29942             this.split.el.setTop(box.y-sh);
29943             this.split.el.setWidth(box.width);
29944         }
29945         if(this.collapsed){
29946             this.updateBody(box.width, null);
29947         }
29948         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29949     }
29950 });
29951
29952 Roo.EastLayoutRegion = function(mgr, config){
29953     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
29954     if(this.split){
29955         this.split.placement = Roo.SplitBar.RIGHT;
29956         this.split.orientation = Roo.SplitBar.HORIZONTAL;
29957         this.split.el.addClass("x-layout-split-h");
29958     }
29959     var size = config.initialSize || config.width;
29960     if(typeof size != "undefined"){
29961         this.el.setWidth(size);
29962     }
29963 };
29964 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
29965     orientation: Roo.SplitBar.HORIZONTAL,
29966     getBox : function(){
29967         if(this.collapsed){
29968             return this.collapsedEl.getBox();
29969         }
29970         var box = this.el.getBox();
29971         if(this.split){
29972             var sw = this.split.el.getWidth();
29973             box.width += sw;
29974             box.x -= sw;
29975         }
29976         return box;
29977     },
29978
29979     updateBox : function(box){
29980         if(this.split && !this.collapsed){
29981             var sw = this.split.el.getWidth();
29982             box.width -= sw;
29983             this.split.el.setLeft(box.x);
29984             this.split.el.setTop(box.y);
29985             this.split.el.setHeight(box.height);
29986             box.x += sw;
29987         }
29988         if(this.collapsed){
29989             this.updateBody(null, box.height);
29990         }
29991         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29992     }
29993 });
29994
29995 Roo.WestLayoutRegion = function(mgr, config){
29996     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
29997     if(this.split){
29998         this.split.placement = Roo.SplitBar.LEFT;
29999         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30000         this.split.el.addClass("x-layout-split-h");
30001     }
30002     var size = config.initialSize || config.width;
30003     if(typeof size != "undefined"){
30004         this.el.setWidth(size);
30005     }
30006 };
30007 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30008     orientation: Roo.SplitBar.HORIZONTAL,
30009     getBox : function(){
30010         if(this.collapsed){
30011             return this.collapsedEl.getBox();
30012         }
30013         var box = this.el.getBox();
30014         if(this.split){
30015             box.width += this.split.el.getWidth();
30016         }
30017         return box;
30018     },
30019     
30020     updateBox : function(box){
30021         if(this.split && !this.collapsed){
30022             var sw = this.split.el.getWidth();
30023             box.width -= sw;
30024             this.split.el.setLeft(box.x+box.width);
30025             this.split.el.setTop(box.y);
30026             this.split.el.setHeight(box.height);
30027         }
30028         if(this.collapsed){
30029             this.updateBody(null, box.height);
30030         }
30031         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30032     }
30033 });
30034 /*
30035  * Based on:
30036  * Ext JS Library 1.1.1
30037  * Copyright(c) 2006-2007, Ext JS, LLC.
30038  *
30039  * Originally Released Under LGPL - original licence link has changed is not relivant.
30040  *
30041  * Fork - LGPL
30042  * <script type="text/javascript">
30043  */
30044  
30045  
30046 /*
30047  * Private internal class for reading and applying state
30048  */
30049 Roo.LayoutStateManager = function(layout){
30050      // default empty state
30051      this.state = {
30052         north: {},
30053         south: {},
30054         east: {},
30055         west: {}       
30056     };
30057 };
30058
30059 Roo.LayoutStateManager.prototype = {
30060     init : function(layout, provider){
30061         this.provider = provider;
30062         var state = provider.get(layout.id+"-layout-state");
30063         if(state){
30064             var wasUpdating = layout.isUpdating();
30065             if(!wasUpdating){
30066                 layout.beginUpdate();
30067             }
30068             for(var key in state){
30069                 if(typeof state[key] != "function"){
30070                     var rstate = state[key];
30071                     var r = layout.getRegion(key);
30072                     if(r && rstate){
30073                         if(rstate.size){
30074                             r.resizeTo(rstate.size);
30075                         }
30076                         if(rstate.collapsed == true){
30077                             r.collapse(true);
30078                         }else{
30079                             r.expand(null, true);
30080                         }
30081                     }
30082                 }
30083             }
30084             if(!wasUpdating){
30085                 layout.endUpdate();
30086             }
30087             this.state = state; 
30088         }
30089         this.layout = layout;
30090         layout.on("regionresized", this.onRegionResized, this);
30091         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30092         layout.on("regionexpanded", this.onRegionExpanded, this);
30093     },
30094     
30095     storeState : function(){
30096         this.provider.set(this.layout.id+"-layout-state", this.state);
30097     },
30098     
30099     onRegionResized : function(region, newSize){
30100         this.state[region.getPosition()].size = newSize;
30101         this.storeState();
30102     },
30103     
30104     onRegionCollapsed : function(region){
30105         this.state[region.getPosition()].collapsed = true;
30106         this.storeState();
30107     },
30108     
30109     onRegionExpanded : function(region){
30110         this.state[region.getPosition()].collapsed = false;
30111         this.storeState();
30112     }
30113 };/*
30114  * Based on:
30115  * Ext JS Library 1.1.1
30116  * Copyright(c) 2006-2007, Ext JS, LLC.
30117  *
30118  * Originally Released Under LGPL - original licence link has changed is not relivant.
30119  *
30120  * Fork - LGPL
30121  * <script type="text/javascript">
30122  */
30123 /**
30124  * @class Roo.ContentPanel
30125  * @extends Roo.util.Observable
30126  * A basic ContentPanel element.
30127  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30128  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30129  * @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
30130  * @cfg {Boolean} closable True if the panel can be closed/removed
30131  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30132  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30133  * @cfg {Toolbar} toolbar A toolbar for this panel
30134  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30135  * @cfg {String} title The title for this panel
30136  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30137  * @cfg {String} url Calls {@link #setUrl} with this value
30138  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30139  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30140  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30141  * @constructor
30142  * Create a new ContentPanel.
30143  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30144  * @param {String/Object} config A string to set only the title or a config object
30145  * @param {String} content (optional) Set the HTML content for this panel
30146  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30147  */
30148 Roo.ContentPanel = function(el, config, content){
30149     
30150      
30151     /*
30152     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30153         config = el;
30154         el = Roo.id();
30155     }
30156     if (config && config.parentLayout) { 
30157         el = config.parentLayout.el.createChild(); 
30158     }
30159     */
30160     if(el.autoCreate){ // xtype is available if this is called from factory
30161         config = el;
30162         el = Roo.id();
30163     }
30164     this.el = Roo.get(el);
30165     if(!this.el && config && config.autoCreate){
30166         if(typeof config.autoCreate == "object"){
30167             if(!config.autoCreate.id){
30168                 config.autoCreate.id = config.id||el;
30169             }
30170             this.el = Roo.DomHelper.append(document.body,
30171                         config.autoCreate, true);
30172         }else{
30173             this.el = Roo.DomHelper.append(document.body,
30174                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30175         }
30176     }
30177     this.closable = false;
30178     this.loaded = false;
30179     this.active = false;
30180     if(typeof config == "string"){
30181         this.title = config;
30182     }else{
30183         Roo.apply(this, config);
30184     }
30185     
30186     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30187         this.wrapEl = this.el.wrap();    
30188         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30189         
30190     }
30191     
30192     
30193     
30194     if(this.resizeEl){
30195         this.resizeEl = Roo.get(this.resizeEl, true);
30196     }else{
30197         this.resizeEl = this.el;
30198     }
30199     this.addEvents({
30200         /**
30201          * @event activate
30202          * Fires when this panel is activated. 
30203          * @param {Roo.ContentPanel} this
30204          */
30205         "activate" : true,
30206         /**
30207          * @event deactivate
30208          * Fires when this panel is activated. 
30209          * @param {Roo.ContentPanel} this
30210          */
30211         "deactivate" : true,
30212
30213         /**
30214          * @event resize
30215          * Fires when this panel is resized if fitToFrame is true.
30216          * @param {Roo.ContentPanel} this
30217          * @param {Number} width The width after any component adjustments
30218          * @param {Number} height The height after any component adjustments
30219          */
30220         "resize" : true
30221     });
30222     if(this.autoScroll){
30223         this.resizeEl.setStyle("overflow", "auto");
30224     }
30225     content = content || this.content;
30226     if(content){
30227         this.setContent(content);
30228     }
30229     if(config && config.url){
30230         this.setUrl(this.url, this.params, this.loadOnce);
30231     }
30232     
30233     
30234     
30235     Roo.ContentPanel.superclass.constructor.call(this);
30236 };
30237
30238 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30239     tabTip:'',
30240     setRegion : function(region){
30241         this.region = region;
30242         if(region){
30243            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30244         }else{
30245            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30246         } 
30247     },
30248     
30249     /**
30250      * Returns the toolbar for this Panel if one was configured. 
30251      * @return {Roo.Toolbar} 
30252      */
30253     getToolbar : function(){
30254         return this.toolbar;
30255     },
30256     
30257     setActiveState : function(active){
30258         this.active = active;
30259         if(!active){
30260             this.fireEvent("deactivate", this);
30261         }else{
30262             this.fireEvent("activate", this);
30263         }
30264     },
30265     /**
30266      * Updates this panel's element
30267      * @param {String} content The new content
30268      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30269     */
30270     setContent : function(content, loadScripts){
30271         this.el.update(content, loadScripts);
30272     },
30273
30274     ignoreResize : function(w, h){
30275         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30276             return true;
30277         }else{
30278             this.lastSize = {width: w, height: h};
30279             return false;
30280         }
30281     },
30282     /**
30283      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30284      * @return {Roo.UpdateManager} The UpdateManager
30285      */
30286     getUpdateManager : function(){
30287         return this.el.getUpdateManager();
30288     },
30289      /**
30290      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30291      * @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:
30292 <pre><code>
30293 panel.load({
30294     url: "your-url.php",
30295     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30296     callback: yourFunction,
30297     scope: yourObject, //(optional scope)
30298     discardUrl: false,
30299     nocache: false,
30300     text: "Loading...",
30301     timeout: 30,
30302     scripts: false
30303 });
30304 </code></pre>
30305      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30306      * 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.
30307      * @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}
30308      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30309      * @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.
30310      * @return {Roo.ContentPanel} this
30311      */
30312     load : function(){
30313         var um = this.el.getUpdateManager();
30314         um.update.apply(um, arguments);
30315         return this;
30316     },
30317
30318
30319     /**
30320      * 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.
30321      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
30322      * @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)
30323      * @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)
30324      * @return {Roo.UpdateManager} The UpdateManager
30325      */
30326     setUrl : function(url, params, loadOnce){
30327         if(this.refreshDelegate){
30328             this.removeListener("activate", this.refreshDelegate);
30329         }
30330         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30331         this.on("activate", this.refreshDelegate);
30332         return this.el.getUpdateManager();
30333     },
30334     
30335     _handleRefresh : function(url, params, loadOnce){
30336         if(!loadOnce || !this.loaded){
30337             var updater = this.el.getUpdateManager();
30338             updater.update(url, params, this._setLoaded.createDelegate(this));
30339         }
30340     },
30341     
30342     _setLoaded : function(){
30343         this.loaded = true;
30344     }, 
30345     
30346     /**
30347      * Returns this panel's id
30348      * @return {String} 
30349      */
30350     getId : function(){
30351         return this.el.id;
30352     },
30353     
30354     /** 
30355      * Returns this panel's element - used by regiosn to add.
30356      * @return {Roo.Element} 
30357      */
30358     getEl : function(){
30359         return this.wrapEl || this.el;
30360     },
30361     
30362     adjustForComponents : function(width, height){
30363         if(this.resizeEl != this.el){
30364             width -= this.el.getFrameWidth('lr');
30365             height -= this.el.getFrameWidth('tb');
30366         }
30367         if(this.toolbar){
30368             var te = this.toolbar.getEl();
30369             height -= te.getHeight();
30370             te.setWidth(width);
30371         }
30372         if(this.adjustments){
30373             width += this.adjustments[0];
30374             height += this.adjustments[1];
30375         }
30376         return {"width": width, "height": height};
30377     },
30378     
30379     setSize : function(width, height){
30380         if(this.fitToFrame && !this.ignoreResize(width, height)){
30381             if(this.fitContainer && this.resizeEl != this.el){
30382                 this.el.setSize(width, height);
30383             }
30384             var size = this.adjustForComponents(width, height);
30385             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
30386             this.fireEvent('resize', this, size.width, size.height);
30387         }
30388     },
30389     
30390     /**
30391      * Returns this panel's title
30392      * @return {String} 
30393      */
30394     getTitle : function(){
30395         return this.title;
30396     },
30397     
30398     /**
30399      * Set this panel's title
30400      * @param {String} title
30401      */
30402     setTitle : function(title){
30403         this.title = title;
30404         if(this.region){
30405             this.region.updatePanelTitle(this, title);
30406         }
30407     },
30408     
30409     /**
30410      * Returns true is this panel was configured to be closable
30411      * @return {Boolean} 
30412      */
30413     isClosable : function(){
30414         return this.closable;
30415     },
30416     
30417     beforeSlide : function(){
30418         this.el.clip();
30419         this.resizeEl.clip();
30420     },
30421     
30422     afterSlide : function(){
30423         this.el.unclip();
30424         this.resizeEl.unclip();
30425     },
30426     
30427     /**
30428      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
30429      *   Will fail silently if the {@link #setUrl} method has not been called.
30430      *   This does not activate the panel, just updates its content.
30431      */
30432     refresh : function(){
30433         if(this.refreshDelegate){
30434            this.loaded = false;
30435            this.refreshDelegate();
30436         }
30437     },
30438     
30439     /**
30440      * Destroys this panel
30441      */
30442     destroy : function(){
30443         this.el.removeAllListeners();
30444         var tempEl = document.createElement("span");
30445         tempEl.appendChild(this.el.dom);
30446         tempEl.innerHTML = "";
30447         this.el.remove();
30448         this.el = null;
30449     },
30450     
30451       /**
30452      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
30453      * <pre><code>
30454
30455 layout.addxtype({
30456        xtype : 'Form',
30457        items: [ .... ]
30458    }
30459 );
30460
30461 </code></pre>
30462      * @param {Object} cfg Xtype definition of item to add.
30463      */
30464     
30465     addxtype : function(cfg) {
30466         // add form..
30467         if (cfg.xtype.match(/^Form$/)) {
30468             var el = this.el.createChild();
30469
30470             this.form = new  Roo.form.Form(cfg);
30471             
30472             
30473             if ( this.form.allItems.length) this.form.render(el.dom);
30474             return this.form;
30475         }
30476         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
30477             // views..
30478             cfg.el = this.el.appendChild(document.createElement("div"));
30479             // factory?
30480             var ret = new Roo[cfg.xtype](cfg);
30481             ret.render(false, ''); // render blank..
30482             return ret;
30483             
30484         }
30485         return false;
30486         
30487     }
30488 });
30489
30490 /**
30491  * @class Roo.GridPanel
30492  * @extends Roo.ContentPanel
30493  * @constructor
30494  * Create a new GridPanel.
30495  * @param {Roo.grid.Grid} grid The grid for this panel
30496  * @param {String/Object} config A string to set only the panel's title, or a config object
30497  */
30498 Roo.GridPanel = function(grid, config){
30499     
30500   
30501     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
30502         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
30503         
30504     this.wrapper.dom.appendChild(grid.getGridEl().dom);
30505     
30506     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
30507     
30508     if(this.toolbar){
30509         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
30510     }
30511     // xtype created footer. - not sure if will work as we normally have to render first..
30512     if (this.footer && !this.footer.el && this.footer.xtype) {
30513         
30514         this.footer.container = this.grid.getView().getFooterPanel(true);
30515         this.footer.dataSource = this.grid.dataSource;
30516         this.footer = Roo.factory(this.footer, Roo);
30517         
30518     }
30519     
30520     grid.monitorWindowResize = false; // turn off autosizing
30521     grid.autoHeight = false;
30522     grid.autoWidth = false;
30523     this.grid = grid;
30524     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
30525 };
30526
30527 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
30528     getId : function(){
30529         return this.grid.id;
30530     },
30531     
30532     /**
30533      * Returns the grid for this panel
30534      * @return {Roo.grid.Grid} 
30535      */
30536     getGrid : function(){
30537         return this.grid;    
30538     },
30539     
30540     setSize : function(width, height){
30541         if(!this.ignoreResize(width, height)){
30542             var grid = this.grid;
30543             var size = this.adjustForComponents(width, height);
30544             grid.getGridEl().setSize(size.width, size.height);
30545             grid.autoSize();
30546         }
30547     },
30548     
30549     beforeSlide : function(){
30550         this.grid.getView().scroller.clip();
30551     },
30552     
30553     afterSlide : function(){
30554         this.grid.getView().scroller.unclip();
30555     },
30556     
30557     destroy : function(){
30558         this.grid.destroy();
30559         delete this.grid;
30560         Roo.GridPanel.superclass.destroy.call(this); 
30561     }
30562 });
30563
30564
30565 /**
30566  * @class Roo.NestedLayoutPanel
30567  * @extends Roo.ContentPanel
30568  * @constructor
30569  * Create a new NestedLayoutPanel.
30570  * 
30571  * 
30572  * @param {Roo.BorderLayout} layout The layout for this panel
30573  * @param {String/Object} config A string to set only the title or a config object
30574  */
30575 Roo.NestedLayoutPanel = function(layout, config)
30576 {
30577     // construct with only one argument..
30578     /* FIXME - implement nicer consturctors
30579     if (layout.layout) {
30580         config = layout;
30581         layout = config.layout;
30582         delete config.layout;
30583     }
30584     if (layout.xtype && !layout.getEl) {
30585         // then layout needs constructing..
30586         layout = Roo.factory(layout, Roo);
30587     }
30588     */
30589     
30590     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
30591     
30592     layout.monitorWindowResize = false; // turn off autosizing
30593     this.layout = layout;
30594     this.layout.getEl().addClass("x-layout-nested-layout");
30595     
30596     
30597     
30598 };
30599
30600 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
30601
30602     setSize : function(width, height){
30603         if(!this.ignoreResize(width, height)){
30604             var size = this.adjustForComponents(width, height);
30605             var el = this.layout.getEl();
30606             el.setSize(size.width, size.height);
30607             var touch = el.dom.offsetWidth;
30608             this.layout.layout();
30609             // ie requires a double layout on the first pass
30610             if(Roo.isIE && !this.initialized){
30611                 this.initialized = true;
30612                 this.layout.layout();
30613             }
30614         }
30615     },
30616     
30617     // activate all subpanels if not currently active..
30618     
30619     setActiveState : function(active){
30620         this.active = active;
30621         if(!active){
30622             this.fireEvent("deactivate", this);
30623             return;
30624         }
30625         
30626         this.fireEvent("activate", this);
30627         // not sure if this should happen before or after..
30628         if (!this.layout) {
30629             return; // should not happen..
30630         }
30631         var reg = false;
30632         for (var r in this.layout.regions) {
30633             reg = this.layout.getRegion(r);
30634             if (reg.getActivePanel()) {
30635                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
30636                 reg.setActivePanel(reg.getActivePanel());
30637                 continue;
30638             }
30639             if (!reg.panels.length) {
30640                 continue;
30641             }
30642             reg.showPanel(reg.getPanel(0));
30643         }
30644         
30645         
30646         
30647         
30648     },
30649     
30650     /**
30651      * Returns the nested BorderLayout for this panel
30652      * @return {Roo.BorderLayout} 
30653      */
30654     getLayout : function(){
30655         return this.layout;
30656     },
30657     
30658      /**
30659      * Adds a xtype elements to the layout of the nested panel
30660      * <pre><code>
30661
30662 panel.addxtype({
30663        xtype : 'ContentPanel',
30664        region: 'west',
30665        items: [ .... ]
30666    }
30667 );
30668
30669 panel.addxtype({
30670         xtype : 'NestedLayoutPanel',
30671         region: 'west',
30672         layout: {
30673            center: { },
30674            west: { }   
30675         },
30676         items : [ ... list of content panels or nested layout panels.. ]
30677    }
30678 );
30679 </code></pre>
30680      * @param {Object} cfg Xtype definition of item to add.
30681      */
30682     addxtype : function(cfg) {
30683         return this.layout.addxtype(cfg);
30684     
30685     }
30686 });
30687
30688 Roo.ScrollPanel = function(el, config, content){
30689     config = config || {};
30690     config.fitToFrame = true;
30691     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
30692     
30693     this.el.dom.style.overflow = "hidden";
30694     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
30695     this.el.removeClass("x-layout-inactive-content");
30696     this.el.on("mousewheel", this.onWheel, this);
30697
30698     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
30699     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
30700     up.unselectable(); down.unselectable();
30701     up.on("click", this.scrollUp, this);
30702     down.on("click", this.scrollDown, this);
30703     up.addClassOnOver("x-scroller-btn-over");
30704     down.addClassOnOver("x-scroller-btn-over");
30705     up.addClassOnClick("x-scroller-btn-click");
30706     down.addClassOnClick("x-scroller-btn-click");
30707     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
30708
30709     this.resizeEl = this.el;
30710     this.el = wrap; this.up = up; this.down = down;
30711 };
30712
30713 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
30714     increment : 100,
30715     wheelIncrement : 5,
30716     scrollUp : function(){
30717         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
30718     },
30719
30720     scrollDown : function(){
30721         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
30722     },
30723
30724     afterScroll : function(){
30725         var el = this.resizeEl;
30726         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
30727         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
30728         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
30729     },
30730
30731     setSize : function(){
30732         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
30733         this.afterScroll();
30734     },
30735
30736     onWheel : function(e){
30737         var d = e.getWheelDelta();
30738         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
30739         this.afterScroll();
30740         e.stopEvent();
30741     },
30742
30743     setContent : function(content, loadScripts){
30744         this.resizeEl.update(content, loadScripts);
30745     }
30746
30747 });
30748
30749
30750
30751
30752
30753
30754
30755
30756
30757 /**
30758  * @class Roo.TreePanel
30759  * @extends Roo.ContentPanel
30760  * @constructor
30761  * Create a new TreePanel.
30762  * @param {String/Object} config A string to set only the panel's title, or a config object
30763  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
30764  */
30765 Roo.TreePanel = function(config){
30766     var el = config.el;
30767     var tree = config.tree;
30768     delete config.tree; 
30769     delete config.el; // hopefull!
30770     Roo.TreePanel.superclass.constructor.call(this, el, config);
30771     var treeEl = el.createChild();
30772     this.tree = new Roo.tree.TreePanel(treeEl , tree);
30773     //console.log(tree);
30774     this.on('activate', function()
30775     {
30776         if (this.tree.rendered) {
30777             return;
30778         }
30779         //console.log('render tree');
30780         this.tree.render();
30781     });
30782     
30783     this.on('resize',  function (cp, w, h) {
30784             this.tree.innerCt.setWidth(w);
30785             this.tree.innerCt.setHeight(h);
30786             this.tree.innerCt.setStyle('overflow-y', 'auto');
30787     });
30788
30789         
30790     
30791 };
30792
30793 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
30794
30795
30796
30797
30798
30799
30800
30801
30802
30803
30804
30805 /*
30806  * Based on:
30807  * Ext JS Library 1.1.1
30808  * Copyright(c) 2006-2007, Ext JS, LLC.
30809  *
30810  * Originally Released Under LGPL - original licence link has changed is not relivant.
30811  *
30812  * Fork - LGPL
30813  * <script type="text/javascript">
30814  */
30815  
30816
30817 /**
30818  * @class Roo.ReaderLayout
30819  * @extends Roo.BorderLayout
30820  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
30821  * center region containing two nested regions (a top one for a list view and one for item preview below),
30822  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
30823  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
30824  * expedites the setup of the overall layout and regions for this common application style.
30825  * Example:
30826  <pre><code>
30827 var reader = new Roo.ReaderLayout();
30828 var CP = Roo.ContentPanel;  // shortcut for adding
30829
30830 reader.beginUpdate();
30831 reader.add("north", new CP("north", "North"));
30832 reader.add("west", new CP("west", {title: "West"}));
30833 reader.add("east", new CP("east", {title: "East"}));
30834
30835 reader.regions.listView.add(new CP("listView", "List"));
30836 reader.regions.preview.add(new CP("preview", "Preview"));
30837 reader.endUpdate();
30838 </code></pre>
30839 * @constructor
30840 * Create a new ReaderLayout
30841 * @param {Object} config Configuration options
30842 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
30843 * document.body if omitted)
30844 */
30845 Roo.ReaderLayout = function(config, renderTo){
30846     var c = config || {size:{}};
30847     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
30848         north: c.north !== false ? Roo.apply({
30849             split:false,
30850             initialSize: 32,
30851             titlebar: false
30852         }, c.north) : false,
30853         west: c.west !== false ? Roo.apply({
30854             split:true,
30855             initialSize: 200,
30856             minSize: 175,
30857             maxSize: 400,
30858             titlebar: true,
30859             collapsible: true,
30860             animate: true,
30861             margins:{left:5,right:0,bottom:5,top:5},
30862             cmargins:{left:5,right:5,bottom:5,top:5}
30863         }, c.west) : false,
30864         east: c.east !== false ? Roo.apply({
30865             split:true,
30866             initialSize: 200,
30867             minSize: 175,
30868             maxSize: 400,
30869             titlebar: true,
30870             collapsible: true,
30871             animate: true,
30872             margins:{left:0,right:5,bottom:5,top:5},
30873             cmargins:{left:5,right:5,bottom:5,top:5}
30874         }, c.east) : false,
30875         center: Roo.apply({
30876             tabPosition: 'top',
30877             autoScroll:false,
30878             closeOnTab: true,
30879             titlebar:false,
30880             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
30881         }, c.center)
30882     });
30883
30884     this.el.addClass('x-reader');
30885
30886     this.beginUpdate();
30887
30888     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
30889         south: c.preview !== false ? Roo.apply({
30890             split:true,
30891             initialSize: 200,
30892             minSize: 100,
30893             autoScroll:true,
30894             collapsible:true,
30895             titlebar: true,
30896             cmargins:{top:5,left:0, right:0, bottom:0}
30897         }, c.preview) : false,
30898         center: Roo.apply({
30899             autoScroll:false,
30900             titlebar:false,
30901             minHeight:200
30902         }, c.listView)
30903     });
30904     this.add('center', new Roo.NestedLayoutPanel(inner,
30905             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
30906
30907     this.endUpdate();
30908
30909     this.regions.preview = inner.getRegion('south');
30910     this.regions.listView = inner.getRegion('center');
30911 };
30912
30913 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
30914  * Based on:
30915  * Ext JS Library 1.1.1
30916  * Copyright(c) 2006-2007, Ext JS, LLC.
30917  *
30918  * Originally Released Under LGPL - original licence link has changed is not relivant.
30919  *
30920  * Fork - LGPL
30921  * <script type="text/javascript">
30922  */
30923  
30924 /**
30925  * @class Roo.grid.Grid
30926  * @extends Roo.util.Observable
30927  * This class represents the primary interface of a component based grid control.
30928  * <br><br>Usage:<pre><code>
30929  var grid = new Roo.grid.Grid("my-container-id", {
30930      ds: myDataStore,
30931      cm: myColModel,
30932      selModel: mySelectionModel,
30933      autoSizeColumns: true,
30934      monitorWindowResize: false,
30935      trackMouseOver: true
30936  });
30937  // set any options
30938  grid.render();
30939  * </code></pre>
30940  * <b>Common Problems:</b><br/>
30941  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
30942  * element will correct this<br/>
30943  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
30944  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
30945  * are unpredictable.<br/>
30946  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
30947  * grid to calculate dimensions/offsets.<br/>
30948   * @constructor
30949  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
30950  * The container MUST have some type of size defined for the grid to fill. The container will be
30951  * automatically set to position relative if it isn't already.
30952  * @param {Object} config A config object that sets properties on this grid.
30953  */
30954 Roo.grid.Grid = function(container, config){
30955         // initialize the container
30956         this.container = Roo.get(container);
30957         this.container.update("");
30958         this.container.setStyle("overflow", "hidden");
30959     this.container.addClass('x-grid-container');
30960
30961     this.id = this.container.id;
30962
30963     Roo.apply(this, config);
30964     // check and correct shorthanded configs
30965     if(this.ds){
30966         this.dataSource = this.ds;
30967         delete this.ds;
30968     }
30969     if(this.cm){
30970         this.colModel = this.cm;
30971         delete this.cm;
30972     }
30973     if(this.sm){
30974         this.selModel = this.sm;
30975         delete this.sm;
30976     }
30977
30978     if (this.selModel) {
30979         this.selModel = Roo.factory(this.selModel, Roo.grid);
30980         this.sm = this.selModel;
30981         this.sm.xmodule = this.xmodule || false;
30982     }
30983     if (typeof(this.colModel.config) == 'undefined') {
30984         this.colModel = new Roo.grid.ColumnModel(this.colModel);
30985         this.cm = this.colModel;
30986         this.cm.xmodule = this.xmodule || false;
30987     }
30988     if (this.dataSource) {
30989         this.dataSource= Roo.factory(this.dataSource, Roo.data);
30990         this.ds = this.dataSource;
30991         this.ds.xmodule = this.xmodule || false;
30992         
30993     }
30994     
30995     
30996     
30997     if(this.width){
30998         this.container.setWidth(this.width);
30999     }
31000
31001     if(this.height){
31002         this.container.setHeight(this.height);
31003     }
31004     /** @private */
31005         this.addEvents({
31006             // raw events
31007             /**
31008              * @event click
31009              * The raw click event for the entire grid.
31010              * @param {Roo.EventObject} e
31011              */
31012             "click" : true,
31013             /**
31014              * @event dblclick
31015              * The raw dblclick event for the entire grid.
31016              * @param {Roo.EventObject} e
31017              */
31018             "dblclick" : true,
31019             /**
31020              * @event contextmenu
31021              * The raw contextmenu event for the entire grid.
31022              * @param {Roo.EventObject} e
31023              */
31024             "contextmenu" : true,
31025             /**
31026              * @event mousedown
31027              * The raw mousedown event for the entire grid.
31028              * @param {Roo.EventObject} e
31029              */
31030             "mousedown" : true,
31031             /**
31032              * @event mouseup
31033              * The raw mouseup event for the entire grid.
31034              * @param {Roo.EventObject} e
31035              */
31036             "mouseup" : true,
31037             /**
31038              * @event mouseover
31039              * The raw mouseover event for the entire grid.
31040              * @param {Roo.EventObject} e
31041              */
31042             "mouseover" : true,
31043             /**
31044              * @event mouseout
31045              * The raw mouseout event for the entire grid.
31046              * @param {Roo.EventObject} e
31047              */
31048             "mouseout" : true,
31049             /**
31050              * @event keypress
31051              * The raw keypress event for the entire grid.
31052              * @param {Roo.EventObject} e
31053              */
31054             "keypress" : true,
31055             /**
31056              * @event keydown
31057              * The raw keydown event for the entire grid.
31058              * @param {Roo.EventObject} e
31059              */
31060             "keydown" : true,
31061
31062             // custom events
31063
31064             /**
31065              * @event cellclick
31066              * Fires when a cell is clicked
31067              * @param {Grid} this
31068              * @param {Number} rowIndex
31069              * @param {Number} columnIndex
31070              * @param {Roo.EventObject} e
31071              */
31072             "cellclick" : true,
31073             /**
31074              * @event celldblclick
31075              * Fires when a cell is double clicked
31076              * @param {Grid} this
31077              * @param {Number} rowIndex
31078              * @param {Number} columnIndex
31079              * @param {Roo.EventObject} e
31080              */
31081             "celldblclick" : true,
31082             /**
31083              * @event rowclick
31084              * Fires when a row is clicked
31085              * @param {Grid} this
31086              * @param {Number} rowIndex
31087              * @param {Roo.EventObject} e
31088              */
31089             "rowclick" : true,
31090             /**
31091              * @event rowdblclick
31092              * Fires when a row is double clicked
31093              * @param {Grid} this
31094              * @param {Number} rowIndex
31095              * @param {Roo.EventObject} e
31096              */
31097             "rowdblclick" : true,
31098             /**
31099              * @event headerclick
31100              * Fires when a header is clicked
31101              * @param {Grid} this
31102              * @param {Number} columnIndex
31103              * @param {Roo.EventObject} e
31104              */
31105             "headerclick" : true,
31106             /**
31107              * @event headerdblclick
31108              * Fires when a header cell is double clicked
31109              * @param {Grid} this
31110              * @param {Number} columnIndex
31111              * @param {Roo.EventObject} e
31112              */
31113             "headerdblclick" : true,
31114             /**
31115              * @event rowcontextmenu
31116              * Fires when a row is right clicked
31117              * @param {Grid} this
31118              * @param {Number} rowIndex
31119              * @param {Roo.EventObject} e
31120              */
31121             "rowcontextmenu" : true,
31122             /**
31123          * @event cellcontextmenu
31124          * Fires when a cell is right clicked
31125          * @param {Grid} this
31126          * @param {Number} rowIndex
31127          * @param {Number} cellIndex
31128          * @param {Roo.EventObject} e
31129          */
31130          "cellcontextmenu" : true,
31131             /**
31132              * @event headercontextmenu
31133              * Fires when a header is right clicked
31134              * @param {Grid} this
31135              * @param {Number} columnIndex
31136              * @param {Roo.EventObject} e
31137              */
31138             "headercontextmenu" : true,
31139             /**
31140              * @event bodyscroll
31141              * Fires when the body element is scrolled
31142              * @param {Number} scrollLeft
31143              * @param {Number} scrollTop
31144              */
31145             "bodyscroll" : true,
31146             /**
31147              * @event columnresize
31148              * Fires when the user resizes a column
31149              * @param {Number} columnIndex
31150              * @param {Number} newSize
31151              */
31152             "columnresize" : true,
31153             /**
31154              * @event columnmove
31155              * Fires when the user moves a column
31156              * @param {Number} oldIndex
31157              * @param {Number} newIndex
31158              */
31159             "columnmove" : true,
31160             /**
31161              * @event startdrag
31162              * Fires when row(s) start being dragged
31163              * @param {Grid} this
31164              * @param {Roo.GridDD} dd The drag drop object
31165              * @param {event} e The raw browser event
31166              */
31167             "startdrag" : true,
31168             /**
31169              * @event enddrag
31170              * Fires when a drag operation is complete
31171              * @param {Grid} this
31172              * @param {Roo.GridDD} dd The drag drop object
31173              * @param {event} e The raw browser event
31174              */
31175             "enddrag" : true,
31176             /**
31177              * @event dragdrop
31178              * Fires when dragged row(s) are dropped on a valid DD target
31179              * @param {Grid} this
31180              * @param {Roo.GridDD} dd The drag drop object
31181              * @param {String} targetId The target drag drop object
31182              * @param {event} e The raw browser event
31183              */
31184             "dragdrop" : true,
31185             /**
31186              * @event dragover
31187              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31188              * @param {Grid} this
31189              * @param {Roo.GridDD} dd The drag drop object
31190              * @param {String} targetId The target drag drop object
31191              * @param {event} e The raw browser event
31192              */
31193             "dragover" : true,
31194             /**
31195              * @event dragenter
31196              *  Fires when the dragged row(s) first cross another DD target while being dragged
31197              * @param {Grid} this
31198              * @param {Roo.GridDD} dd The drag drop object
31199              * @param {String} targetId The target drag drop object
31200              * @param {event} e The raw browser event
31201              */
31202             "dragenter" : true,
31203             /**
31204              * @event dragout
31205              * Fires when the dragged row(s) leave another DD target while being dragged
31206              * @param {Grid} this
31207              * @param {Roo.GridDD} dd The drag drop object
31208              * @param {String} targetId The target drag drop object
31209              * @param {event} e The raw browser event
31210              */
31211             "dragout" : true,
31212         /**
31213          * @event render
31214          * Fires when the grid is rendered
31215          * @param {Grid} grid
31216          */
31217         render : true
31218     });
31219
31220     Roo.grid.Grid.superclass.constructor.call(this);
31221 };
31222 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31223     /**
31224      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31225          */
31226         minColumnWidth : 25,
31227
31228     /**
31229          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31230          * <b>on initial render.</b> It is more efficient to explicitly size the columns
31231          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31232          */
31233         autoSizeColumns : false,
31234
31235         /**
31236          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31237          */
31238         autoSizeHeaders : true,
31239
31240         /**
31241          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31242          */
31243         monitorWindowResize : true,
31244
31245         /**
31246          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31247          * rows measured to get a columns size. Default is 0 (all rows).
31248          */
31249         maxRowsToMeasure : 0,
31250
31251         /**
31252          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31253          */
31254         trackMouseOver : true,
31255
31256         /**
31257          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
31258          */
31259         enableDragDrop : false,
31260
31261         /**
31262          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
31263          */
31264         enableColumnMove : true,
31265
31266         /**
31267          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
31268          */
31269         enableColumnHide : true,
31270
31271         /**
31272          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
31273          */
31274         enableRowHeightSync : false,
31275
31276         /**
31277          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
31278          */
31279         stripeRows : true,
31280
31281         /**
31282          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
31283          */
31284         autoHeight : false,
31285
31286     /**
31287      * @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.
31288      */
31289     autoExpandColumn : false,
31290
31291     /**
31292     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
31293     * Default is 50.
31294     */
31295     autoExpandMin : 50,
31296
31297     /**
31298     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
31299     */
31300     autoExpandMax : 1000,
31301
31302     /**
31303          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
31304          */
31305         view : null,
31306
31307         /**
31308      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
31309          */
31310         loadMask : false,
31311
31312     // private
31313     rendered : false,
31314
31315     /**
31316     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
31317     * of a fixed width. Default is false.
31318     */
31319     /**
31320     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
31321     */
31322     /**
31323      * Called once after all setup has been completed and the grid is ready to be rendered.
31324      * @return {Roo.grid.Grid} this
31325      */
31326     render : function(){
31327         var c = this.container;
31328         // try to detect autoHeight/width mode
31329         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
31330             this.autoHeight = true;
31331         }
31332         var view = this.getView();
31333         view.init(this);
31334
31335         c.on("click", this.onClick, this);
31336         c.on("dblclick", this.onDblClick, this);
31337         c.on("contextmenu", this.onContextMenu, this);
31338         c.on("keydown", this.onKeyDown, this);
31339
31340         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
31341
31342         this.getSelectionModel().init(this);
31343
31344         view.render();
31345
31346         if(this.loadMask){
31347             this.loadMask = new Roo.LoadMask(this.container,
31348                     Roo.apply({store:this.dataSource}, this.loadMask));
31349         }
31350         
31351         
31352         if (this.toolbar && this.toolbar.xtype) {
31353             this.toolbar.container = this.getView().getHeaderPanel(true);
31354             this.toolbar = new Ext.Toolbar(this.toolbar);
31355         }
31356         if (this.footer && this.footer.xtype) {
31357             this.footer.dataSource = this.getDataSource();
31358             this.footer.container = this.getView().getFooterPanel(true);
31359             this.footer = Roo.factory(this.footer, Roo);
31360         }
31361         this.rendered = true;
31362         this.fireEvent('render', this);
31363         return this;
31364     },
31365
31366         /**
31367          * Reconfigures the grid to use a different Store and Column Model.
31368          * The View will be bound to the new objects and refreshed.
31369          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
31370          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
31371          */
31372     reconfigure : function(dataSource, colModel){
31373         if(this.loadMask){
31374             this.loadMask.destroy();
31375             this.loadMask = new Roo.LoadMask(this.container,
31376                     Roo.apply({store:dataSource}, this.loadMask));
31377         }
31378         this.view.bind(dataSource, colModel);
31379         this.dataSource = dataSource;
31380         this.colModel = colModel;
31381         this.view.refresh(true);
31382     },
31383
31384     // private
31385     onKeyDown : function(e){
31386         this.fireEvent("keydown", e);
31387     },
31388
31389     /**
31390      * Destroy this grid.
31391      * @param {Boolean} removeEl True to remove the element
31392      */
31393     destroy : function(removeEl, keepListeners){
31394         if(this.loadMask){
31395             this.loadMask.destroy();
31396         }
31397         var c = this.container;
31398         c.removeAllListeners();
31399         this.view.destroy();
31400         this.colModel.purgeListeners();
31401         if(!keepListeners){
31402             this.purgeListeners();
31403         }
31404         c.update("");
31405         if(removeEl === true){
31406             c.remove();
31407         }
31408     },
31409
31410     // private
31411     processEvent : function(name, e){
31412         this.fireEvent(name, e);
31413         var t = e.getTarget();
31414         var v = this.view;
31415         var header = v.findHeaderIndex(t);
31416         if(header !== false){
31417             this.fireEvent("header" + name, this, header, e);
31418         }else{
31419             var row = v.findRowIndex(t);
31420             var cell = v.findCellIndex(t);
31421             if(row !== false){
31422                 this.fireEvent("row" + name, this, row, e);
31423                 if(cell !== false){
31424                     this.fireEvent("cell" + name, this, row, cell, e);
31425                 }
31426             }
31427         }
31428     },
31429
31430     // private
31431     onClick : function(e){
31432         this.processEvent("click", e);
31433     },
31434
31435     // private
31436     onContextMenu : function(e, t){
31437         this.processEvent("contextmenu", e);
31438     },
31439
31440     // private
31441     onDblClick : function(e){
31442         this.processEvent("dblclick", e);
31443     },
31444
31445     // private
31446     walkCells : function(row, col, step, fn, scope){
31447         var cm = this.colModel, clen = cm.getColumnCount();
31448         var ds = this.dataSource, rlen = ds.getCount(), first = true;
31449         if(step < 0){
31450             if(col < 0){
31451                 row--;
31452                 first = false;
31453             }
31454             while(row >= 0){
31455                 if(!first){
31456                     col = clen-1;
31457                 }
31458                 first = false;
31459                 while(col >= 0){
31460                     if(fn.call(scope || this, row, col, cm) === true){
31461                         return [row, col];
31462                     }
31463                     col--;
31464                 }
31465                 row--;
31466             }
31467         } else {
31468             if(col >= clen){
31469                 row++;
31470                 first = false;
31471             }
31472             while(row < rlen){
31473                 if(!first){
31474                     col = 0;
31475                 }
31476                 first = false;
31477                 while(col < clen){
31478                     if(fn.call(scope || this, row, col, cm) === true){
31479                         return [row, col];
31480                     }
31481                     col++;
31482                 }
31483                 row++;
31484             }
31485         }
31486         return null;
31487     },
31488
31489     // private
31490     getSelections : function(){
31491         return this.selModel.getSelections();
31492     },
31493
31494     /**
31495      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
31496      * but if manual update is required this method will initiate it.
31497      */
31498     autoSize : function(){
31499         if(this.rendered){
31500             this.view.layout();
31501             if(this.view.adjustForScroll){
31502                 this.view.adjustForScroll();
31503             }
31504         }
31505     },
31506
31507     /**
31508      * Returns the grid's underlying element.
31509      * @return {Element} The element
31510      */
31511     getGridEl : function(){
31512         return this.container;
31513     },
31514
31515     // private for compatibility, overridden by editor grid
31516     stopEditing : function(){},
31517
31518     /**
31519      * Returns the grid's SelectionModel.
31520      * @return {SelectionModel}
31521      */
31522     getSelectionModel : function(){
31523         if(!this.selModel){
31524             this.selModel = new Roo.grid.RowSelectionModel();
31525         }
31526         return this.selModel;
31527     },
31528
31529     /**
31530      * Returns the grid's DataSource.
31531      * @return {DataSource}
31532      */
31533     getDataSource : function(){
31534         return this.dataSource;
31535     },
31536
31537     /**
31538      * Returns the grid's ColumnModel.
31539      * @return {ColumnModel}
31540      */
31541     getColumnModel : function(){
31542         return this.colModel;
31543     },
31544
31545     /**
31546      * Returns the grid's GridView object.
31547      * @return {GridView}
31548      */
31549     getView : function(){
31550         if(!this.view){
31551             this.view = new Roo.grid.GridView(this.viewConfig);
31552         }
31553         return this.view;
31554     },
31555     /**
31556      * Called to get grid's drag proxy text, by default returns this.ddText.
31557      * @return {String}
31558      */
31559     getDragDropText : function(){
31560         var count = this.selModel.getCount();
31561         return String.format(this.ddText, count, count == 1 ? '' : 's');
31562     }
31563 });
31564 /**
31565  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
31566  * %0 is replaced with the number of selected rows.
31567  * @type String
31568  */
31569 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
31570  * Based on:
31571  * Ext JS Library 1.1.1
31572  * Copyright(c) 2006-2007, Ext JS, LLC.
31573  *
31574  * Originally Released Under LGPL - original licence link has changed is not relivant.
31575  *
31576  * Fork - LGPL
31577  * <script type="text/javascript">
31578  */
31579  
31580 Roo.grid.AbstractGridView = function(){
31581         this.grid = null;
31582         
31583         this.events = {
31584             "beforerowremoved" : true,
31585             "beforerowsinserted" : true,
31586             "beforerefresh" : true,
31587             "rowremoved" : true,
31588             "rowsinserted" : true,
31589             "rowupdated" : true,
31590             "refresh" : true
31591         };
31592     Roo.grid.AbstractGridView.superclass.constructor.call(this);
31593 };
31594
31595 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
31596     rowClass : "x-grid-row",
31597     cellClass : "x-grid-cell",
31598     tdClass : "x-grid-td",
31599     hdClass : "x-grid-hd",
31600     splitClass : "x-grid-hd-split",
31601     
31602         init: function(grid){
31603         this.grid = grid;
31604                 var cid = this.grid.getGridEl().id;
31605         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
31606         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
31607         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
31608         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
31609         },
31610         
31611         getColumnRenderers : function(){
31612         var renderers = [];
31613         var cm = this.grid.colModel;
31614         var colCount = cm.getColumnCount();
31615         for(var i = 0; i < colCount; i++){
31616             renderers[i] = cm.getRenderer(i);
31617         }
31618         return renderers;
31619     },
31620     
31621     getColumnIds : function(){
31622         var ids = [];
31623         var cm = this.grid.colModel;
31624         var colCount = cm.getColumnCount();
31625         for(var i = 0; i < colCount; i++){
31626             ids[i] = cm.getColumnId(i);
31627         }
31628         return ids;
31629     },
31630     
31631     getDataIndexes : function(){
31632         if(!this.indexMap){
31633             this.indexMap = this.buildIndexMap();
31634         }
31635         return this.indexMap.colToData;
31636     },
31637     
31638     getColumnIndexByDataIndex : function(dataIndex){
31639         if(!this.indexMap){
31640             this.indexMap = this.buildIndexMap();
31641         }
31642         return this.indexMap.dataToCol[dataIndex];
31643     },
31644     
31645     /**
31646      * Set a css style for a column dynamically. 
31647      * @param {Number} colIndex The index of the column
31648      * @param {String} name The css property name
31649      * @param {String} value The css value
31650      */
31651     setCSSStyle : function(colIndex, name, value){
31652         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
31653         Roo.util.CSS.updateRule(selector, name, value);
31654     },
31655     
31656     generateRules : function(cm){
31657         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
31658         Roo.util.CSS.removeStyleSheet(rulesId);
31659         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
31660             var cid = cm.getColumnId(i);
31661             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
31662                          this.tdSelector, cid, " {\n}\n",
31663                          this.hdSelector, cid, " {\n}\n",
31664                          this.splitSelector, cid, " {\n}\n");
31665         }
31666         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
31667     }
31668 });/*
31669  * Based on:
31670  * Ext JS Library 1.1.1
31671  * Copyright(c) 2006-2007, Ext JS, LLC.
31672  *
31673  * Originally Released Under LGPL - original licence link has changed is not relivant.
31674  *
31675  * Fork - LGPL
31676  * <script type="text/javascript">
31677  */
31678
31679 // private
31680 // This is a support class used internally by the Grid components
31681 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
31682     this.grid = grid;
31683     this.view = grid.getView();
31684     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
31685     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
31686     if(hd2){
31687         this.setHandleElId(Roo.id(hd));
31688         this.setOuterHandleElId(Roo.id(hd2));
31689     }
31690     this.scroll = false;
31691 };
31692 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
31693     maxDragWidth: 120,
31694     getDragData : function(e){
31695         var t = Roo.lib.Event.getTarget(e);
31696         var h = this.view.findHeaderCell(t);
31697         if(h){
31698             return {ddel: h.firstChild, header:h};
31699         }
31700         return false;
31701     },
31702
31703     onInitDrag : function(e){
31704         this.view.headersDisabled = true;
31705         var clone = this.dragData.ddel.cloneNode(true);
31706         clone.id = Roo.id();
31707         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
31708         this.proxy.update(clone);
31709         return true;
31710     },
31711
31712     afterValidDrop : function(){
31713         var v = this.view;
31714         setTimeout(function(){
31715             v.headersDisabled = false;
31716         }, 50);
31717     },
31718
31719     afterInvalidDrop : function(){
31720         var v = this.view;
31721         setTimeout(function(){
31722             v.headersDisabled = false;
31723         }, 50);
31724     }
31725 });
31726 /*
31727  * Based on:
31728  * Ext JS Library 1.1.1
31729  * Copyright(c) 2006-2007, Ext JS, LLC.
31730  *
31731  * Originally Released Under LGPL - original licence link has changed is not relivant.
31732  *
31733  * Fork - LGPL
31734  * <script type="text/javascript">
31735  */
31736 // private
31737 // This is a support class used internally by the Grid components
31738 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
31739     this.grid = grid;
31740     this.view = grid.getView();
31741     // split the proxies so they don't interfere with mouse events
31742     this.proxyTop = Roo.DomHelper.append(document.body, {
31743         cls:"col-move-top", html:"&#160;"
31744     }, true);
31745     this.proxyBottom = Roo.DomHelper.append(document.body, {
31746         cls:"col-move-bottom", html:"&#160;"
31747     }, true);
31748     this.proxyTop.hide = this.proxyBottom.hide = function(){
31749         this.setLeftTop(-100,-100);
31750         this.setStyle("visibility", "hidden");
31751     };
31752     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
31753     // temporarily disabled
31754     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
31755     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
31756 };
31757 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
31758     proxyOffsets : [-4, -9],
31759     fly: Roo.Element.fly,
31760
31761     getTargetFromEvent : function(e){
31762         var t = Roo.lib.Event.getTarget(e);
31763         var cindex = this.view.findCellIndex(t);
31764         if(cindex !== false){
31765             return this.view.getHeaderCell(cindex);
31766         }
31767     },
31768
31769     nextVisible : function(h){
31770         var v = this.view, cm = this.grid.colModel;
31771         h = h.nextSibling;
31772         while(h){
31773             if(!cm.isHidden(v.getCellIndex(h))){
31774                 return h;
31775             }
31776             h = h.nextSibling;
31777         }
31778         return null;
31779     },
31780
31781     prevVisible : function(h){
31782         var v = this.view, cm = this.grid.colModel;
31783         h = h.prevSibling;
31784         while(h){
31785             if(!cm.isHidden(v.getCellIndex(h))){
31786                 return h;
31787             }
31788             h = h.prevSibling;
31789         }
31790         return null;
31791     },
31792
31793     positionIndicator : function(h, n, e){
31794         var x = Roo.lib.Event.getPageX(e);
31795         var r = Roo.lib.Dom.getRegion(n.firstChild);
31796         var px, pt, py = r.top + this.proxyOffsets[1];
31797         if((r.right - x) <= (r.right-r.left)/2){
31798             px = r.right+this.view.borderWidth;
31799             pt = "after";
31800         }else{
31801             px = r.left;
31802             pt = "before";
31803         }
31804         var oldIndex = this.view.getCellIndex(h);
31805         var newIndex = this.view.getCellIndex(n);
31806
31807         if(this.grid.colModel.isFixed(newIndex)){
31808             return false;
31809         }
31810
31811         var locked = this.grid.colModel.isLocked(newIndex);
31812
31813         if(pt == "after"){
31814             newIndex++;
31815         }
31816         if(oldIndex < newIndex){
31817             newIndex--;
31818         }
31819         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
31820             return false;
31821         }
31822         px +=  this.proxyOffsets[0];
31823         this.proxyTop.setLeftTop(px, py);
31824         this.proxyTop.show();
31825         if(!this.bottomOffset){
31826             this.bottomOffset = this.view.mainHd.getHeight();
31827         }
31828         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
31829         this.proxyBottom.show();
31830         return pt;
31831     },
31832
31833     onNodeEnter : function(n, dd, e, data){
31834         if(data.header != n){
31835             this.positionIndicator(data.header, n, e);
31836         }
31837     },
31838
31839     onNodeOver : function(n, dd, e, data){
31840         var result = false;
31841         if(data.header != n){
31842             result = this.positionIndicator(data.header, n, e);
31843         }
31844         if(!result){
31845             this.proxyTop.hide();
31846             this.proxyBottom.hide();
31847         }
31848         return result ? this.dropAllowed : this.dropNotAllowed;
31849     },
31850
31851     onNodeOut : function(n, dd, e, data){
31852         this.proxyTop.hide();
31853         this.proxyBottom.hide();
31854     },
31855
31856     onNodeDrop : function(n, dd, e, data){
31857         var h = data.header;
31858         if(h != n){
31859             var cm = this.grid.colModel;
31860             var x = Roo.lib.Event.getPageX(e);
31861             var r = Roo.lib.Dom.getRegion(n.firstChild);
31862             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
31863             var oldIndex = this.view.getCellIndex(h);
31864             var newIndex = this.view.getCellIndex(n);
31865             var locked = cm.isLocked(newIndex);
31866             if(pt == "after"){
31867                 newIndex++;
31868             }
31869             if(oldIndex < newIndex){
31870                 newIndex--;
31871             }
31872             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
31873                 return false;
31874             }
31875             cm.setLocked(oldIndex, locked, true);
31876             cm.moveColumn(oldIndex, newIndex);
31877             this.grid.fireEvent("columnmove", oldIndex, newIndex);
31878             return true;
31879         }
31880         return false;
31881     }
31882 });
31883 /*
31884  * Based on:
31885  * Ext JS Library 1.1.1
31886  * Copyright(c) 2006-2007, Ext JS, LLC.
31887  *
31888  * Originally Released Under LGPL - original licence link has changed is not relivant.
31889  *
31890  * Fork - LGPL
31891  * <script type="text/javascript">
31892  */
31893   
31894 /**
31895  * @class Roo.grid.GridView
31896  * @extends Roo.util.Observable
31897  *
31898  * @constructor
31899  * @param {Object} config
31900  */
31901 Roo.grid.GridView = function(config){
31902     Roo.grid.GridView.superclass.constructor.call(this);
31903     this.el = null;
31904
31905     Roo.apply(this, config);
31906 };
31907
31908 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
31909
31910     /**
31911      * Override this function to apply custom css classes to rows during rendering
31912      * @param {Record} record The record
31913      * @param {Number} index
31914      * @method getRowClass
31915      */
31916     rowClass : "x-grid-row",
31917
31918     cellClass : "x-grid-col",
31919
31920     tdClass : "x-grid-td",
31921
31922     hdClass : "x-grid-hd",
31923
31924     splitClass : "x-grid-split",
31925
31926     sortClasses : ["sort-asc", "sort-desc"],
31927
31928     enableMoveAnim : false,
31929
31930     hlColor: "C3DAF9",
31931
31932     dh : Roo.DomHelper,
31933
31934     fly : Roo.Element.fly,
31935
31936     css : Roo.util.CSS,
31937
31938     borderWidth: 1,
31939
31940     splitOffset: 3,
31941
31942     scrollIncrement : 22,
31943
31944     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
31945
31946     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
31947
31948     bind : function(ds, cm){
31949         if(this.ds){
31950             this.ds.un("load", this.onLoad, this);
31951             this.ds.un("datachanged", this.onDataChange, this);
31952             this.ds.un("add", this.onAdd, this);
31953             this.ds.un("remove", this.onRemove, this);
31954             this.ds.un("update", this.onUpdate, this);
31955             this.ds.un("clear", this.onClear, this);
31956         }
31957         if(ds){
31958             ds.on("load", this.onLoad, this);
31959             ds.on("datachanged", this.onDataChange, this);
31960             ds.on("add", this.onAdd, this);
31961             ds.on("remove", this.onRemove, this);
31962             ds.on("update", this.onUpdate, this);
31963             ds.on("clear", this.onClear, this);
31964         }
31965         this.ds = ds;
31966
31967         if(this.cm){
31968             this.cm.un("widthchange", this.onColWidthChange, this);
31969             this.cm.un("headerchange", this.onHeaderChange, this);
31970             this.cm.un("hiddenchange", this.onHiddenChange, this);
31971             this.cm.un("columnmoved", this.onColumnMove, this);
31972             this.cm.un("columnlockchange", this.onColumnLock, this);
31973         }
31974         if(cm){
31975             this.generateRules(cm);
31976             cm.on("widthchange", this.onColWidthChange, this);
31977             cm.on("headerchange", this.onHeaderChange, this);
31978             cm.on("hiddenchange", this.onHiddenChange, this);
31979             cm.on("columnmoved", this.onColumnMove, this);
31980             cm.on("columnlockchange", this.onColumnLock, this);
31981         }
31982         this.cm = cm;
31983     },
31984
31985     init: function(grid){
31986                 Roo.grid.GridView.superclass.init.call(this, grid);
31987
31988                 this.bind(grid.dataSource, grid.colModel);
31989
31990             grid.on("headerclick", this.handleHeaderClick, this);
31991
31992         if(grid.trackMouseOver){
31993             grid.on("mouseover", this.onRowOver, this);
31994                 grid.on("mouseout", this.onRowOut, this);
31995             }
31996             grid.cancelTextSelection = function(){};
31997                 this.gridId = grid.id;
31998
31999                 var tpls = this.templates || {};
32000
32001                 if(!tpls.master){
32002                     tpls.master = new Roo.Template(
32003                        '<div class="x-grid" hidefocus="true">',
32004                           '<div class="x-grid-topbar"></div>',
32005                           '<div class="x-grid-scroller"><div></div></div>',
32006                           '<div class="x-grid-locked">',
32007                               '<div class="x-grid-header">{lockedHeader}</div>',
32008                               '<div class="x-grid-body">{lockedBody}</div>',
32009                           "</div>",
32010                           '<div class="x-grid-viewport">',
32011                               '<div class="x-grid-header">{header}</div>',
32012                               '<div class="x-grid-body">{body}</div>',
32013                           "</div>",
32014                           '<div class="x-grid-bottombar"></div>',
32015                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32016                           '<div class="x-grid-resize-proxy">&#160;</div>',
32017                        "</div>"
32018                     );
32019                     tpls.master.disableformats = true;
32020                 }
32021
32022                 if(!tpls.header){
32023                     tpls.header = new Roo.Template(
32024                        '<table border="0" cellspacing="0" cellpadding="0">',
32025                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32026                        "</table>{splits}"
32027                     );
32028                     tpls.header.disableformats = true;
32029                 }
32030                 tpls.header.compile();
32031
32032                 if(!tpls.hcell){
32033                     tpls.hcell = new Roo.Template(
32034                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32035                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32036                         "</div></td>"
32037                      );
32038                      tpls.hcell.disableFormats = true;
32039                 }
32040                 tpls.hcell.compile();
32041
32042                 if(!tpls.hsplit){
32043                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32044                     tpls.hsplit.disableFormats = true;
32045                 }
32046                 tpls.hsplit.compile();
32047
32048                 if(!tpls.body){
32049                     tpls.body = new Roo.Template(
32050                        '<table border="0" cellspacing="0" cellpadding="0">',
32051                        "<tbody>{rows}</tbody>",
32052                        "</table>"
32053                     );
32054                     tpls.body.disableFormats = true;
32055                 }
32056                 tpls.body.compile();
32057
32058                 if(!tpls.row){
32059                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32060                     tpls.row.disableFormats = true;
32061                 }
32062                 tpls.row.compile();
32063
32064                 if(!tpls.cell){
32065                     tpls.cell = new Roo.Template(
32066                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32067                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32068                         "</td>"
32069                     );
32070             tpls.cell.disableFormats = true;
32071         }
32072                 tpls.cell.compile();
32073
32074                 this.templates = tpls;
32075         },
32076
32077         // remap these for backwards compat
32078     onColWidthChange : function(){
32079         this.updateColumns.apply(this, arguments);
32080     },
32081     onHeaderChange : function(){
32082         this.updateHeaders.apply(this, arguments);
32083     }, 
32084     onHiddenChange : function(){
32085         this.handleHiddenChange.apply(this, arguments);
32086     },
32087     onColumnMove : function(){
32088         this.handleColumnMove.apply(this, arguments);
32089     },
32090     onColumnLock : function(){
32091         this.handleLockChange.apply(this, arguments);
32092     },
32093
32094     onDataChange : function(){
32095         this.refresh();
32096         this.updateHeaderSortState();
32097     },
32098
32099         onClear : function(){
32100         this.refresh();
32101     },
32102
32103         onUpdate : function(ds, record){
32104         this.refreshRow(record);
32105     },
32106
32107     refreshRow : function(record){
32108         var ds = this.ds, index;
32109         if(typeof record == 'number'){
32110             index = record;
32111             record = ds.getAt(index);
32112         }else{
32113             index = ds.indexOf(record);
32114         }
32115         this.insertRows(ds, index, index, true);
32116         this.onRemove(ds, record, index+1, true);
32117         this.syncRowHeights(index, index);
32118         this.layout();
32119         this.fireEvent("rowupdated", this, index, record);
32120     },
32121
32122     onAdd : function(ds, records, index){
32123         this.insertRows(ds, index, index + (records.length-1));
32124     },
32125
32126     onRemove : function(ds, record, index, isUpdate){
32127         if(isUpdate !== true){
32128             this.fireEvent("beforerowremoved", this, index, record);
32129         }
32130         var bt = this.getBodyTable(), lt = this.getLockedTable();
32131         if(bt.rows[index]){
32132             bt.firstChild.removeChild(bt.rows[index]);
32133         }
32134         if(lt.rows[index]){
32135             lt.firstChild.removeChild(lt.rows[index]);
32136         }
32137         if(isUpdate !== true){
32138             this.stripeRows(index);
32139             this.syncRowHeights(index, index);
32140             this.layout();
32141             this.fireEvent("rowremoved", this, index, record);
32142         }
32143     },
32144
32145     onLoad : function(){
32146         this.scrollToTop();
32147     },
32148
32149     /**
32150      * Scrolls the grid to the top
32151      */
32152     scrollToTop : function(){
32153         if(this.scroller){
32154             this.scroller.dom.scrollTop = 0;
32155             this.syncScroll();
32156         }
32157     },
32158
32159     /**
32160      * Gets a panel in the header of the grid that can be used for toolbars etc.
32161      * After modifying the contents of this panel a call to grid.autoSize() may be
32162      * required to register any changes in size.
32163      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32164      * @return Roo.Element
32165      */
32166     getHeaderPanel : function(doShow){
32167         if(doShow){
32168             this.headerPanel.show();
32169         }
32170         return this.headerPanel;
32171         },
32172
32173         /**
32174      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32175      * After modifying the contents of this panel a call to grid.autoSize() may be
32176      * required to register any changes in size.
32177      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32178      * @return Roo.Element
32179      */
32180     getFooterPanel : function(doShow){
32181         if(doShow){
32182             this.footerPanel.show();
32183         }
32184         return this.footerPanel;
32185         },
32186
32187         initElements : function(){
32188             var E = Roo.Element;
32189             var el = this.grid.getGridEl().dom.firstChild;
32190             var cs = el.childNodes;
32191
32192             this.el = new E(el);
32193             this.headerPanel = new E(el.firstChild);
32194             this.headerPanel.enableDisplayMode("block");
32195
32196         this.scroller = new E(cs[1]);
32197             this.scrollSizer = new E(this.scroller.dom.firstChild);
32198
32199             this.lockedWrap = new E(cs[2]);
32200             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
32201             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
32202
32203             this.mainWrap = new E(cs[3]);
32204             this.mainHd = new E(this.mainWrap.dom.firstChild);
32205             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
32206
32207             this.footerPanel = new E(cs[4]);
32208             this.footerPanel.enableDisplayMode("block");
32209
32210         this.focusEl = new E(cs[5]);
32211         this.focusEl.swallowEvent("click", true);
32212         this.resizeProxy = new E(cs[6]);
32213
32214             this.headerSelector = String.format(
32215                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
32216                this.lockedHd.id, this.mainHd.id
32217             );
32218
32219             this.splitterSelector = String.format(
32220                '#{0} div.x-grid-split, #{1} div.x-grid-split',
32221                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
32222             );
32223     },
32224     idToCssName : function(s)
32225     {
32226         return s.replace(/[^a-z0-9]+/ig, '-');
32227     },
32228
32229         getHeaderCell : function(index){
32230             return Roo.DomQuery.select(this.headerSelector)[index];
32231         },
32232
32233         getHeaderCellMeasure : function(index){
32234             return this.getHeaderCell(index).firstChild;
32235         },
32236
32237         getHeaderCellText : function(index){
32238             return this.getHeaderCell(index).firstChild.firstChild;
32239         },
32240
32241         getLockedTable : function(){
32242             return this.lockedBody.dom.firstChild;
32243         },
32244
32245         getBodyTable : function(){
32246             return this.mainBody.dom.firstChild;
32247         },
32248
32249         getLockedRow : function(index){
32250             return this.getLockedTable().rows[index];
32251         },
32252
32253         getRow : function(index){
32254             return this.getBodyTable().rows[index];
32255         },
32256
32257         getRowComposite : function(index){
32258             if(!this.rowEl){
32259                 this.rowEl = new Roo.CompositeElementLite();
32260             }
32261         var els = [], lrow, mrow;
32262         if(lrow = this.getLockedRow(index)){
32263             els.push(lrow);
32264         }
32265         if(mrow = this.getRow(index)){
32266             els.push(mrow);
32267         }
32268         this.rowEl.elements = els;
32269             return this.rowEl;
32270         },
32271
32272         getCell : function(rowIndex, colIndex){
32273             var locked = this.cm.getLockedCount();
32274             var source;
32275             if(colIndex < locked){
32276                 source = this.lockedBody.dom.firstChild;
32277             }else{
32278                 source = this.mainBody.dom.firstChild;
32279                 colIndex -= locked;
32280             }
32281         return source.rows[rowIndex].childNodes[colIndex];
32282         },
32283
32284         getCellText : function(rowIndex, colIndex){
32285             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
32286         },
32287
32288         getCellBox : function(cell){
32289             var b = this.fly(cell).getBox();
32290         if(Roo.isOpera){ // opera fails to report the Y
32291             b.y = cell.offsetTop + this.mainBody.getY();
32292         }
32293         return b;
32294     },
32295
32296     getCellIndex : function(cell){
32297         var id = String(cell.className).match(this.cellRE);
32298         if(id){
32299             return parseInt(id[1], 10);
32300         }
32301         return 0;
32302     },
32303
32304     findHeaderIndex : function(n){
32305         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32306         return r ? this.getCellIndex(r) : false;
32307     },
32308
32309     findHeaderCell : function(n){
32310         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32311         return r ? r : false;
32312     },
32313
32314     findRowIndex : function(n){
32315         if(!n){
32316             return false;
32317         }
32318         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
32319         return r ? r.rowIndex : false;
32320     },
32321
32322     findCellIndex : function(node){
32323         var stop = this.el.dom;
32324         while(node && node != stop){
32325             if(this.findRE.test(node.className)){
32326                 return this.getCellIndex(node);
32327             }
32328             node = node.parentNode;
32329         }
32330         return false;
32331     },
32332
32333     getColumnId : function(index){
32334             return this.cm.getColumnId(index);
32335         },
32336
32337         getSplitters : function(){
32338             if(this.splitterSelector){
32339                return Roo.DomQuery.select(this.splitterSelector);
32340             }else{
32341                 return null;
32342             }
32343         },
32344
32345         getSplitter : function(index){
32346             return this.getSplitters()[index];
32347         },
32348
32349     onRowOver : function(e, t){
32350         var row;
32351         if((row = this.findRowIndex(t)) !== false){
32352             this.getRowComposite(row).addClass("x-grid-row-over");
32353         }
32354     },
32355
32356     onRowOut : function(e, t){
32357         var row;
32358         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
32359             this.getRowComposite(row).removeClass("x-grid-row-over");
32360         }
32361     },
32362
32363     renderHeaders : function(){
32364             var cm = this.cm;
32365         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
32366         var cb = [], lb = [], sb = [], lsb = [], p = {};
32367         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32368             p.cellId = "x-grid-hd-0-" + i;
32369             p.splitId = "x-grid-csplit-0-" + i;
32370             p.id = cm.getColumnId(i);
32371             p.title = cm.getColumnTooltip(i) || "";
32372             p.value = cm.getColumnHeader(i) || "";
32373             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
32374             if(!cm.isLocked(i)){
32375                 cb[cb.length] = ct.apply(p);
32376                 sb[sb.length] = st.apply(p);
32377             }else{
32378                 lb[lb.length] = ct.apply(p);
32379                 lsb[lsb.length] = st.apply(p);
32380             }
32381         }
32382         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
32383                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
32384         },
32385
32386         updateHeaders : function(){
32387         var html = this.renderHeaders();
32388         this.lockedHd.update(html[0]);
32389         this.mainHd.update(html[1]);
32390     },
32391
32392     /**
32393      * Focuses the specified row.
32394      * @param {Number} row The row index
32395      */
32396     focusRow : function(row){
32397         var x = this.scroller.dom.scrollLeft;
32398         this.focusCell(row, 0, false);
32399         this.scroller.dom.scrollLeft = x;
32400     },
32401
32402     /**
32403      * Focuses the specified cell.
32404      * @param {Number} row The row index
32405      * @param {Number} col The column index
32406      * @param {Boolean} hscroll false to disable horizontal scrolling
32407      */
32408     focusCell : function(row, col, hscroll){
32409         var el = this.ensureVisible(row, col, hscroll);
32410         this.focusEl.alignTo(el, "tl-tl");
32411         if(Roo.isGecko){
32412             this.focusEl.focus();
32413         }else{
32414             this.focusEl.focus.defer(1, this.focusEl);
32415         }
32416     },
32417
32418     /**
32419      * Scrolls the specified cell into view
32420      * @param {Number} row The row index
32421      * @param {Number} col The column index
32422      * @param {Boolean} hscroll false to disable horizontal scrolling
32423      */
32424     ensureVisible : function(row, col, hscroll){
32425         if(typeof row != "number"){
32426             row = row.rowIndex;
32427         }
32428         if(row < 0 && row >= this.ds.getCount()){
32429             return;
32430         }
32431         col = (col !== undefined ? col : 0);
32432         var cm = this.grid.colModel;
32433         while(cm.isHidden(col)){
32434             col++;
32435         }
32436
32437         var el = this.getCell(row, col);
32438         if(!el){
32439             return;
32440         }
32441         var c = this.scroller.dom;
32442
32443         var ctop = parseInt(el.offsetTop, 10);
32444         var cleft = parseInt(el.offsetLeft, 10);
32445         var cbot = ctop + el.offsetHeight;
32446         var cright = cleft + el.offsetWidth;
32447
32448         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
32449         var stop = parseInt(c.scrollTop, 10);
32450         var sleft = parseInt(c.scrollLeft, 10);
32451         var sbot = stop + ch;
32452         var sright = sleft + c.clientWidth;
32453
32454         if(ctop < stop){
32455                 c.scrollTop = ctop;
32456         }else if(cbot > sbot){
32457             c.scrollTop = cbot-ch;
32458         }
32459
32460         if(hscroll !== false){
32461             if(cleft < sleft){
32462                 c.scrollLeft = cleft;
32463             }else if(cright > sright){
32464                 c.scrollLeft = cright-c.clientWidth;
32465             }
32466         }
32467         return el;
32468     },
32469
32470     updateColumns : function(){
32471         this.grid.stopEditing();
32472         var cm = this.grid.colModel, colIds = this.getColumnIds();
32473         //var totalWidth = cm.getTotalWidth();
32474         var pos = 0;
32475         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32476             //if(cm.isHidden(i)) continue;
32477             var w = cm.getColumnWidth(i);
32478             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32479             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32480         }
32481         this.updateSplitters();
32482     },
32483
32484     generateRules : function(cm){
32485         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
32486         Roo.util.CSS.removeStyleSheet(rulesId);
32487         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32488             var cid = cm.getColumnId(i);
32489             var align = '';
32490             if(cm.config[i].align){
32491                 align = 'text-align:'+cm.config[i].align+';';
32492             }
32493             var hidden = '';
32494             if(cm.isHidden(i)){
32495                 hidden = 'display:none;';
32496             }
32497             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
32498             ruleBuf.push(
32499                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
32500                     this.hdSelector, cid, " {\n", align, width, "}\n",
32501                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
32502                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
32503         }
32504         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32505     },
32506
32507     updateSplitters : function(){
32508         var cm = this.cm, s = this.getSplitters();
32509         if(s){ // splitters not created yet
32510             var pos = 0, locked = true;
32511             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32512                 if(cm.isHidden(i)) continue;
32513                 var w = cm.getColumnWidth(i);
32514                 if(!cm.isLocked(i) && locked){
32515                     pos = 0;
32516                     locked = false;
32517                 }
32518                 pos += w;
32519                 s[i].style.left = (pos-this.splitOffset) + "px";
32520             }
32521         }
32522     },
32523
32524     handleHiddenChange : function(colModel, colIndex, hidden){
32525         if(hidden){
32526             this.hideColumn(colIndex);
32527         }else{
32528             this.unhideColumn(colIndex);
32529         }
32530     },
32531
32532     hideColumn : function(colIndex){
32533         var cid = this.getColumnId(colIndex);
32534         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
32535         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
32536         if(Roo.isSafari){
32537             this.updateHeaders();
32538         }
32539         this.updateSplitters();
32540         this.layout();
32541     },
32542
32543     unhideColumn : function(colIndex){
32544         var cid = this.getColumnId(colIndex);
32545         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
32546         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
32547
32548         if(Roo.isSafari){
32549             this.updateHeaders();
32550         }
32551         this.updateSplitters();
32552         this.layout();
32553     },
32554
32555     insertRows : function(dm, firstRow, lastRow, isUpdate){
32556         if(firstRow == 0 && lastRow == dm.getCount()-1){
32557             this.refresh();
32558         }else{
32559             if(!isUpdate){
32560                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
32561             }
32562             var s = this.getScrollState();
32563             var markup = this.renderRows(firstRow, lastRow);
32564             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
32565             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
32566             this.restoreScroll(s);
32567             if(!isUpdate){
32568                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
32569                 this.syncRowHeights(firstRow, lastRow);
32570                 this.stripeRows(firstRow);
32571                 this.layout();
32572             }
32573         }
32574     },
32575
32576     bufferRows : function(markup, target, index){
32577         var before = null, trows = target.rows, tbody = target.tBodies[0];
32578         if(index < trows.length){
32579             before = trows[index];
32580         }
32581         var b = document.createElement("div");
32582         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
32583         var rows = b.firstChild.rows;
32584         for(var i = 0, len = rows.length; i < len; i++){
32585             if(before){
32586                 tbody.insertBefore(rows[0], before);
32587             }else{
32588                 tbody.appendChild(rows[0]);
32589             }
32590         }
32591         b.innerHTML = "";
32592         b = null;
32593     },
32594
32595     deleteRows : function(dm, firstRow, lastRow){
32596         if(dm.getRowCount()<1){
32597             this.fireEvent("beforerefresh", this);
32598             this.mainBody.update("");
32599             this.lockedBody.update("");
32600             this.fireEvent("refresh", this);
32601         }else{
32602             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
32603             var bt = this.getBodyTable();
32604             var tbody = bt.firstChild;
32605             var rows = bt.rows;
32606             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
32607                 tbody.removeChild(rows[firstRow]);
32608             }
32609             this.stripeRows(firstRow);
32610             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
32611         }
32612     },
32613
32614     updateRows : function(dataSource, firstRow, lastRow){
32615         var s = this.getScrollState();
32616         this.refresh();
32617         this.restoreScroll(s);
32618     },
32619
32620     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
32621         if(!noRefresh){
32622            this.refresh();
32623         }
32624         this.updateHeaderSortState();
32625     },
32626
32627     getScrollState : function(){
32628         var sb = this.scroller.dom;
32629         return {left: sb.scrollLeft, top: sb.scrollTop};
32630     },
32631
32632     stripeRows : function(startRow){
32633         if(!this.grid.stripeRows || this.ds.getCount() < 1){
32634             return;
32635         }
32636         startRow = startRow || 0;
32637         var rows = this.getBodyTable().rows;
32638         var lrows = this.getLockedTable().rows;
32639         var cls = ' x-grid-row-alt ';
32640         for(var i = startRow, len = rows.length; i < len; i++){
32641             var row = rows[i], lrow = lrows[i];
32642             var isAlt = ((i+1) % 2 == 0);
32643             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
32644             if(isAlt == hasAlt){
32645                 continue;
32646             }
32647             if(isAlt){
32648                 row.className += " x-grid-row-alt";
32649             }else{
32650                 row.className = row.className.replace("x-grid-row-alt", "");
32651             }
32652             if(lrow){
32653                 lrow.className = row.className;
32654             }
32655         }
32656     },
32657
32658     restoreScroll : function(state){
32659         var sb = this.scroller.dom;
32660         sb.scrollLeft = state.left;
32661         sb.scrollTop = state.top;
32662         this.syncScroll();
32663     },
32664
32665     syncScroll : function(){
32666         var sb = this.scroller.dom;
32667         var sh = this.mainHd.dom;
32668         var bs = this.mainBody.dom;
32669         var lv = this.lockedBody.dom;
32670         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
32671         lv.scrollTop = bs.scrollTop = sb.scrollTop;
32672     },
32673
32674     handleScroll : function(e){
32675         this.syncScroll();
32676         var sb = this.scroller.dom;
32677         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
32678         e.stopEvent();
32679     },
32680
32681     handleWheel : function(e){
32682         var d = e.getWheelDelta();
32683         this.scroller.dom.scrollTop -= d*22;
32684         // set this here to prevent jumpy scrolling on large tables
32685         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
32686         e.stopEvent();
32687     },
32688
32689     renderRows : function(startRow, endRow){
32690         // pull in all the crap needed to render rows
32691         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
32692         var colCount = cm.getColumnCount();
32693
32694         if(ds.getCount() < 1){
32695             return ["", ""];
32696         }
32697
32698         // build a map for all the columns
32699         var cs = [];
32700         for(var i = 0; i < colCount; i++){
32701             var name = cm.getDataIndex(i);
32702             cs[i] = {
32703                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
32704                 renderer : cm.getRenderer(i),
32705                 id : cm.getColumnId(i),
32706                 locked : cm.isLocked(i)
32707             };
32708         }
32709
32710         startRow = startRow || 0;
32711         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
32712
32713         // records to render
32714         var rs = ds.getRange(startRow, endRow);
32715
32716         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
32717     },
32718
32719     // As much as I hate to duplicate code, this was branched because FireFox really hates
32720     // [].join("") on strings. The performance difference was substantial enough to
32721     // branch this function
32722     doRender : Roo.isGecko ?
32723             function(cs, rs, ds, startRow, colCount, stripe){
32724                 var ts = this.templates, ct = ts.cell, rt = ts.row;
32725                 // buffers
32726                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
32727                 for(var j = 0, len = rs.length; j < len; j++){
32728                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
32729                     for(var i = 0; i < colCount; i++){
32730                         c = cs[i];
32731                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
32732                         p.id = c.id;
32733                         p.css = p.attr = "";
32734                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
32735                         if(p.value == undefined || p.value === "") p.value = "&#160;";
32736                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
32737                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
32738                         }
32739                         var markup = ct.apply(p);
32740                         if(!c.locked){
32741                             cb+= markup;
32742                         }else{
32743                             lcb+= markup;
32744                         }
32745                     }
32746                     var alt = [];
32747                     if(stripe && ((rowIndex+1) % 2 == 0)){
32748                         alt[0] = "x-grid-row-alt";
32749                     }
32750                     if(r.dirty){
32751                         alt[1] = " x-grid-dirty-row";
32752                     }
32753                     rp.cells = lcb;
32754                     if(this.getRowClass){
32755                         alt[2] = this.getRowClass(r, rowIndex);
32756                     }
32757                     rp.alt = alt.join(" ");
32758                     lbuf+= rt.apply(rp);
32759                     rp.cells = cb;
32760                     buf+=  rt.apply(rp);
32761                 }
32762                 return [lbuf, buf];
32763             } :
32764             function(cs, rs, ds, startRow, colCount, stripe){
32765                 var ts = this.templates, ct = ts.cell, rt = ts.row;
32766                 // buffers
32767                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
32768                 for(var j = 0, len = rs.length; j < len; j++){
32769                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
32770                     for(var i = 0; i < colCount; i++){
32771                         c = cs[i];
32772                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
32773                         p.id = c.id;
32774                         p.css = p.attr = "";
32775                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
32776                         if(p.value == undefined || p.value === "") p.value = "&#160;";
32777                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
32778                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
32779                         }
32780                         var markup = ct.apply(p);
32781                         if(!c.locked){
32782                             cb[cb.length] = markup;
32783                         }else{
32784                             lcb[lcb.length] = markup;
32785                         }
32786                     }
32787                     var alt = [];
32788                     if(stripe && ((rowIndex+1) % 2 == 0)){
32789                         alt[0] = "x-grid-row-alt";
32790                     }
32791                     if(r.dirty){
32792                         alt[1] = " x-grid-dirty-row";
32793                     }
32794                     rp.cells = lcb;
32795                     if(this.getRowClass){
32796                         alt[2] = this.getRowClass(r, rowIndex);
32797                     }
32798                     rp.alt = alt.join(" ");
32799                     rp.cells = lcb.join("");
32800                     lbuf[lbuf.length] = rt.apply(rp);
32801                     rp.cells = cb.join("");
32802                     buf[buf.length] =  rt.apply(rp);
32803                 }
32804                 return [lbuf.join(""), buf.join("")];
32805             },
32806
32807     renderBody : function(){
32808         var markup = this.renderRows();
32809         var bt = this.templates.body;
32810         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
32811     },
32812
32813     /**
32814      * Refreshes the grid
32815      * @param {Boolean} headersToo
32816      */
32817     refresh : function(headersToo){
32818         this.fireEvent("beforerefresh", this);
32819         this.grid.stopEditing();
32820         var result = this.renderBody();
32821         this.lockedBody.update(result[0]);
32822         this.mainBody.update(result[1]);
32823         if(headersToo === true){
32824             this.updateHeaders();
32825             this.updateColumns();
32826             this.updateSplitters();
32827             this.updateHeaderSortState();
32828         }
32829         this.syncRowHeights();
32830         this.layout();
32831         this.fireEvent("refresh", this);
32832     },
32833
32834     handleColumnMove : function(cm, oldIndex, newIndex){
32835         this.indexMap = null;
32836         var s = this.getScrollState();
32837         this.refresh(true);
32838         this.restoreScroll(s);
32839         this.afterMove(newIndex);
32840     },
32841
32842     afterMove : function(colIndex){
32843         if(this.enableMoveAnim && Roo.enableFx){
32844             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
32845         }
32846     },
32847
32848     updateCell : function(dm, rowIndex, dataIndex){
32849         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
32850         if(typeof colIndex == "undefined"){ // not present in grid
32851             return;
32852         }
32853         var cm = this.grid.colModel;
32854         var cell = this.getCell(rowIndex, colIndex);
32855         var cellText = this.getCellText(rowIndex, colIndex);
32856
32857         var p = {
32858             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
32859             id : cm.getColumnId(colIndex),
32860             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
32861         };
32862         var renderer = cm.getRenderer(colIndex);
32863         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
32864         if(typeof val == "undefined" || val === "") val = "&#160;";
32865         cellText.innerHTML = val;
32866         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
32867         this.syncRowHeights(rowIndex, rowIndex);
32868     },
32869
32870     calcColumnWidth : function(colIndex, maxRowsToMeasure){
32871         var maxWidth = 0;
32872         if(this.grid.autoSizeHeaders){
32873             var h = this.getHeaderCellMeasure(colIndex);
32874             maxWidth = Math.max(maxWidth, h.scrollWidth);
32875         }
32876         var tb, index;
32877         if(this.cm.isLocked(colIndex)){
32878             tb = this.getLockedTable();
32879             index = colIndex;
32880         }else{
32881             tb = this.getBodyTable();
32882             index = colIndex - this.cm.getLockedCount();
32883         }
32884         if(tb && tb.rows){
32885             var rows = tb.rows;
32886             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
32887             for(var i = 0; i < stopIndex; i++){
32888                 var cell = rows[i].childNodes[index].firstChild;
32889                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
32890             }
32891         }
32892         return maxWidth + /*margin for error in IE*/ 5;
32893     },
32894     /**
32895      * Autofit a column to its content.
32896      * @param {Number} colIndex
32897      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
32898      */
32899      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
32900          if(this.cm.isHidden(colIndex)){
32901              return; // can't calc a hidden column
32902          }
32903         if(forceMinSize){
32904             var cid = this.cm.getColumnId(colIndex);
32905             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
32906            if(this.grid.autoSizeHeaders){
32907                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
32908            }
32909         }
32910         var newWidth = this.calcColumnWidth(colIndex);
32911         this.cm.setColumnWidth(colIndex,
32912             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
32913         if(!suppressEvent){
32914             this.grid.fireEvent("columnresize", colIndex, newWidth);
32915         }
32916     },
32917
32918     /**
32919      * Autofits all columns to their content and then expands to fit any extra space in the grid
32920      */
32921      autoSizeColumns : function(){
32922         var cm = this.grid.colModel;
32923         var colCount = cm.getColumnCount();
32924         for(var i = 0; i < colCount; i++){
32925             this.autoSizeColumn(i, true, true);
32926         }
32927         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
32928             this.fitColumns();
32929         }else{
32930             this.updateColumns();
32931             this.layout();
32932         }
32933     },
32934
32935     /**
32936      * Autofits all columns to the grid's width proportionate with their current size
32937      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
32938      */
32939     fitColumns : function(reserveScrollSpace){
32940         var cm = this.grid.colModel;
32941         var colCount = cm.getColumnCount();
32942         var cols = [];
32943         var width = 0;
32944         var i, w;
32945         for (i = 0; i < colCount; i++){
32946             if(!cm.isHidden(i) && !cm.isFixed(i)){
32947                 w = cm.getColumnWidth(i);
32948                 cols.push(i);
32949                 cols.push(w);
32950                 width += w;
32951             }
32952         }
32953         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
32954         if(reserveScrollSpace){
32955             avail -= 17;
32956         }
32957         var frac = (avail - cm.getTotalWidth())/width;
32958         while (cols.length){
32959             w = cols.pop();
32960             i = cols.pop();
32961             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
32962         }
32963         this.updateColumns();
32964         this.layout();
32965     },
32966
32967     onRowSelect : function(rowIndex){
32968         var row = this.getRowComposite(rowIndex);
32969         row.addClass("x-grid-row-selected");
32970     },
32971
32972     onRowDeselect : function(rowIndex){
32973         var row = this.getRowComposite(rowIndex);
32974         row.removeClass("x-grid-row-selected");
32975     },
32976
32977     onCellSelect : function(row, col){
32978         var cell = this.getCell(row, col);
32979         if(cell){
32980             Roo.fly(cell).addClass("x-grid-cell-selected");
32981         }
32982     },
32983
32984     onCellDeselect : function(row, col){
32985         var cell = this.getCell(row, col);
32986         if(cell){
32987             Roo.fly(cell).removeClass("x-grid-cell-selected");
32988         }
32989     },
32990
32991     updateHeaderSortState : function(){
32992         var state = this.ds.getSortState();
32993         if(!state){
32994             return;
32995         }
32996         this.sortState = state;
32997         var sortColumn = this.cm.findColumnIndex(state.field);
32998         if(sortColumn != -1){
32999             var sortDir = state.direction;
33000             var sc = this.sortClasses;
33001             var hds = this.el.select(this.headerSelector).removeClass(sc);
33002             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33003         }
33004     },
33005
33006     handleHeaderClick : function(g, index){
33007         if(this.headersDisabled){
33008             return;
33009         }
33010         var dm = g.dataSource, cm = g.colModel;
33011             if(!cm.isSortable(index)){
33012             return;
33013         }
33014             g.stopEditing();
33015         dm.sort(cm.getDataIndex(index));
33016     },
33017
33018
33019     destroy : function(){
33020         if(this.colMenu){
33021             this.colMenu.removeAll();
33022             Roo.menu.MenuMgr.unregister(this.colMenu);
33023             this.colMenu.getEl().remove();
33024             delete this.colMenu;
33025         }
33026         if(this.hmenu){
33027             this.hmenu.removeAll();
33028             Roo.menu.MenuMgr.unregister(this.hmenu);
33029             this.hmenu.getEl().remove();
33030             delete this.hmenu;
33031         }
33032         if(this.grid.enableColumnMove){
33033             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33034             if(dds){
33035                 for(var dd in dds){
33036                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33037                         var elid = dds[dd].dragElId;
33038                         dds[dd].unreg();
33039                         Roo.get(elid).remove();
33040                     } else if(dds[dd].config.isTarget){
33041                         dds[dd].proxyTop.remove();
33042                         dds[dd].proxyBottom.remove();
33043                         dds[dd].unreg();
33044                     }
33045                     if(Roo.dd.DDM.locationCache[dd]){
33046                         delete Roo.dd.DDM.locationCache[dd];
33047                     }
33048                 }
33049                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33050             }
33051         }
33052         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33053         this.bind(null, null);
33054         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33055     },
33056
33057     handleLockChange : function(){
33058         this.refresh(true);
33059     },
33060
33061     onDenyColumnLock : function(){
33062
33063     },
33064
33065     onDenyColumnHide : function(){
33066
33067     },
33068
33069     handleHdMenuClick : function(item){
33070         var index = this.hdCtxIndex;
33071         var cm = this.cm, ds = this.ds;
33072         switch(item.id){
33073             case "asc":
33074                 ds.sort(cm.getDataIndex(index), "ASC");
33075                 break;
33076             case "desc":
33077                 ds.sort(cm.getDataIndex(index), "DESC");
33078                 break;
33079             case "lock":
33080                 var lc = cm.getLockedCount();
33081                 if(cm.getColumnCount(true) <= lc+1){
33082                     this.onDenyColumnLock();
33083                     return;
33084                 }
33085                 if(lc != index){
33086                     cm.setLocked(index, true, true);
33087                     cm.moveColumn(index, lc);
33088                     this.grid.fireEvent("columnmove", index, lc);
33089                 }else{
33090                     cm.setLocked(index, true);
33091                 }
33092             break;
33093             case "unlock":
33094                 var lc = cm.getLockedCount();
33095                 if((lc-1) != index){
33096                     cm.setLocked(index, false, true);
33097                     cm.moveColumn(index, lc-1);
33098                     this.grid.fireEvent("columnmove", index, lc-1);
33099                 }else{
33100                     cm.setLocked(index, false);
33101                 }
33102             break;
33103             default:
33104                 index = cm.getIndexById(item.id.substr(4));
33105                 if(index != -1){
33106                     if(item.checked && cm.getColumnCount(true) <= 1){
33107                         this.onDenyColumnHide();
33108                         return false;
33109                     }
33110                     cm.setHidden(index, item.checked);
33111                 }
33112         }
33113         return true;
33114     },
33115
33116     beforeColMenuShow : function(){
33117         var cm = this.cm,  colCount = cm.getColumnCount();
33118         this.colMenu.removeAll();
33119         for(var i = 0; i < colCount; i++){
33120             this.colMenu.add(new Roo.menu.CheckItem({
33121                 id: "col-"+cm.getColumnId(i),
33122                 text: cm.getColumnHeader(i),
33123                 checked: !cm.isHidden(i),
33124                 hideOnClick:false
33125             }));
33126         }
33127     },
33128
33129     handleHdCtx : function(g, index, e){
33130         e.stopEvent();
33131         var hd = this.getHeaderCell(index);
33132         this.hdCtxIndex = index;
33133         var ms = this.hmenu.items, cm = this.cm;
33134         ms.get("asc").setDisabled(!cm.isSortable(index));
33135         ms.get("desc").setDisabled(!cm.isSortable(index));
33136         if(this.grid.enableColLock !== false){
33137             ms.get("lock").setDisabled(cm.isLocked(index));
33138             ms.get("unlock").setDisabled(!cm.isLocked(index));
33139         }
33140         this.hmenu.show(hd, "tl-bl");
33141     },
33142
33143     handleHdOver : function(e){
33144         var hd = this.findHeaderCell(e.getTarget());
33145         if(hd && !this.headersDisabled){
33146             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33147                this.fly(hd).addClass("x-grid-hd-over");
33148             }
33149         }
33150     },
33151
33152     handleHdOut : function(e){
33153         var hd = this.findHeaderCell(e.getTarget());
33154         if(hd){
33155             this.fly(hd).removeClass("x-grid-hd-over");
33156         }
33157     },
33158
33159     handleSplitDblClick : function(e, t){
33160         var i = this.getCellIndex(t);
33161         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33162             this.autoSizeColumn(i, true);
33163             this.layout();
33164         }
33165     },
33166
33167     render : function(){
33168
33169         var cm = this.cm;
33170         var colCount = cm.getColumnCount();
33171
33172         if(this.grid.monitorWindowResize === true){
33173             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33174         }
33175         var header = this.renderHeaders();
33176         var body = this.templates.body.apply({rows:""});
33177         var html = this.templates.master.apply({
33178             lockedBody: body,
33179             body: body,
33180             lockedHeader: header[0],
33181             header: header[1]
33182         });
33183
33184         //this.updateColumns();
33185
33186         this.grid.getGridEl().dom.innerHTML = html;
33187
33188         this.initElements();
33189
33190         this.scroller.on("scroll", this.handleScroll, this);
33191         this.lockedBody.on("mousewheel", this.handleWheel, this);
33192         this.mainBody.on("mousewheel", this.handleWheel, this);
33193
33194         this.mainHd.on("mouseover", this.handleHdOver, this);
33195         this.mainHd.on("mouseout", this.handleHdOut, this);
33196         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
33197                 {delegate: "."+this.splitClass});
33198
33199         this.lockedHd.on("mouseover", this.handleHdOver, this);
33200         this.lockedHd.on("mouseout", this.handleHdOut, this);
33201         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
33202                 {delegate: "."+this.splitClass});
33203
33204         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
33205             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33206         }
33207
33208         this.updateSplitters();
33209
33210         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
33211             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33212             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33213         }
33214
33215         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
33216             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
33217             this.hmenu.add(
33218                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
33219                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
33220             );
33221             if(this.grid.enableColLock !== false){
33222                 this.hmenu.add('-',
33223                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
33224                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
33225                 );
33226             }
33227             if(this.grid.enableColumnHide !== false){
33228
33229                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
33230                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
33231                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
33232
33233                 this.hmenu.add('-',
33234                     {id:"columns", text: this.columnsText, menu: this.colMenu}
33235                 );
33236             }
33237             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
33238
33239             this.grid.on("headercontextmenu", this.handleHdCtx, this);
33240         }
33241
33242         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
33243             this.dd = new Roo.grid.GridDragZone(this.grid, {
33244                 ddGroup : this.grid.ddGroup || 'GridDD'
33245             });
33246         }
33247
33248         /*
33249         for(var i = 0; i < colCount; i++){
33250             if(cm.isHidden(i)){
33251                 this.hideColumn(i);
33252             }
33253             if(cm.config[i].align){
33254                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
33255                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
33256             }
33257         }*/
33258         
33259         this.updateHeaderSortState();
33260
33261         this.beforeInitialResize();
33262         this.layout(true);
33263
33264         // two part rendering gives faster view to the user
33265         this.renderPhase2.defer(1, this);
33266     },
33267
33268     renderPhase2 : function(){
33269         // render the rows now
33270         this.refresh();
33271         if(this.grid.autoSizeColumns){
33272             this.autoSizeColumns();
33273         }
33274     },
33275
33276     beforeInitialResize : function(){
33277
33278     },
33279
33280     onColumnSplitterMoved : function(i, w){
33281         this.userResized = true;
33282         var cm = this.grid.colModel;
33283         cm.setColumnWidth(i, w, true);
33284         var cid = cm.getColumnId(i);
33285         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33286         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33287         this.updateSplitters();
33288         this.layout();
33289         this.grid.fireEvent("columnresize", i, w);
33290     },
33291
33292     syncRowHeights : function(startIndex, endIndex){
33293         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
33294             startIndex = startIndex || 0;
33295             var mrows = this.getBodyTable().rows;
33296             var lrows = this.getLockedTable().rows;
33297             var len = mrows.length-1;
33298             endIndex = Math.min(endIndex || len, len);
33299             for(var i = startIndex; i <= endIndex; i++){
33300                 var m = mrows[i], l = lrows[i];
33301                 var h = Math.max(m.offsetHeight, l.offsetHeight);
33302                 m.style.height = l.style.height = h + "px";
33303             }
33304         }
33305     },
33306
33307     layout : function(initialRender, is2ndPass){
33308         var g = this.grid;
33309         var auto = g.autoHeight;
33310         var scrollOffset = 16;
33311         var c = g.getGridEl(), cm = this.cm,
33312                 expandCol = g.autoExpandColumn,
33313                 gv = this;
33314         //c.beginMeasure();
33315
33316         if(!c.dom.offsetWidth){ // display:none?
33317             if(initialRender){
33318                 this.lockedWrap.show();
33319                 this.mainWrap.show();
33320             }
33321             return;
33322         }
33323
33324         var hasLock = this.cm.isLocked(0);
33325
33326         var tbh = this.headerPanel.getHeight();
33327         var bbh = this.footerPanel.getHeight();
33328
33329         if(auto){
33330             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
33331             var newHeight = ch + c.getBorderWidth("tb");
33332             if(g.maxHeight){
33333                 newHeight = Math.min(g.maxHeight, newHeight);
33334             }
33335             c.setHeight(newHeight);
33336         }
33337
33338         if(g.autoWidth){
33339             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
33340         }
33341
33342         var s = this.scroller;
33343
33344         var csize = c.getSize(true);
33345
33346         this.el.setSize(csize.width, csize.height);
33347
33348         this.headerPanel.setWidth(csize.width);
33349         this.footerPanel.setWidth(csize.width);
33350
33351         var hdHeight = this.mainHd.getHeight();
33352         var vw = csize.width;
33353         var vh = csize.height - (tbh + bbh);
33354
33355         s.setSize(vw, vh);
33356
33357         var bt = this.getBodyTable();
33358         var ltWidth = hasLock ?
33359                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
33360
33361         var scrollHeight = bt.offsetHeight;
33362         var scrollWidth = ltWidth + bt.offsetWidth;
33363         var vscroll = false, hscroll = false;
33364
33365         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
33366
33367         var lw = this.lockedWrap, mw = this.mainWrap;
33368         var lb = this.lockedBody, mb = this.mainBody;
33369
33370         setTimeout(function(){
33371             var t = s.dom.offsetTop;
33372             var w = s.dom.clientWidth,
33373                 h = s.dom.clientHeight;
33374
33375             lw.setTop(t);
33376             lw.setSize(ltWidth, h);
33377
33378             mw.setLeftTop(ltWidth, t);
33379             mw.setSize(w-ltWidth, h);
33380
33381             lb.setHeight(h-hdHeight);
33382             mb.setHeight(h-hdHeight);
33383
33384             if(is2ndPass !== true && !gv.userResized && expandCol){
33385                 // high speed resize without full column calculation
33386                 
33387                 var ci = cm.getIndexById(expandCol);
33388                 if (ci < 0) {
33389                     ci = cm.findColumnIndex(expandCol);
33390                 }
33391                 ci = Math.max(0, ci); // make sure it's got at least the first col.
33392                 var expandId = cm.getColumnId(ci);
33393                 var  tw = cm.getTotalWidth(false);
33394                 var currentWidth = cm.getColumnWidth(ci);
33395                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
33396                 if(currentWidth != cw){
33397                     cm.setColumnWidth(ci, cw, true);
33398                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33399                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33400                     gv.updateSplitters();
33401                     gv.layout(false, true);
33402                 }
33403             }
33404
33405             if(initialRender){
33406                 lw.show();
33407                 mw.show();
33408             }
33409             //c.endMeasure();
33410         }, 10);
33411     },
33412
33413     onWindowResize : function(){
33414         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
33415             return;
33416         }
33417         this.layout();
33418     },
33419
33420     appendFooter : function(parentEl){
33421         return null;
33422     },
33423
33424     sortAscText : "Sort Ascending",
33425     sortDescText : "Sort Descending",
33426     lockText : "Lock Column",
33427     unlockText : "Unlock Column",
33428     columnsText : "Columns"
33429 });
33430
33431
33432 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
33433     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
33434     this.proxy.el.addClass('x-grid3-col-dd');
33435 };
33436
33437 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
33438     handleMouseDown : function(e){
33439
33440     },
33441
33442     callHandleMouseDown : function(e){
33443         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
33444     }
33445 });
33446 /*
33447  * Based on:
33448  * Ext JS Library 1.1.1
33449  * Copyright(c) 2006-2007, Ext JS, LLC.
33450  *
33451  * Originally Released Under LGPL - original licence link has changed is not relivant.
33452  *
33453  * Fork - LGPL
33454  * <script type="text/javascript">
33455  */
33456  
33457 // private
33458 // This is a support class used internally by the Grid components
33459 Roo.grid.SplitDragZone = function(grid, hd, hd2){
33460     this.grid = grid;
33461     this.view = grid.getView();
33462     this.proxy = this.view.resizeProxy;
33463     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
33464         "gridSplitters" + this.grid.getGridEl().id, {
33465         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
33466     });
33467     this.setHandleElId(Roo.id(hd));
33468     this.setOuterHandleElId(Roo.id(hd2));
33469     this.scroll = false;
33470 };
33471 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
33472     fly: Roo.Element.fly,
33473
33474     b4StartDrag : function(x, y){
33475         this.view.headersDisabled = true;
33476         this.proxy.setHeight(this.view.mainWrap.getHeight());
33477         var w = this.cm.getColumnWidth(this.cellIndex);
33478         var minw = Math.max(w-this.grid.minColumnWidth, 0);
33479         this.resetConstraints();
33480         this.setXConstraint(minw, 1000);
33481         this.setYConstraint(0, 0);
33482         this.minX = x - minw;
33483         this.maxX = x + 1000;
33484         this.startPos = x;
33485         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
33486     },
33487
33488
33489     handleMouseDown : function(e){
33490         ev = Roo.EventObject.setEvent(e);
33491         var t = this.fly(ev.getTarget());
33492         if(t.hasClass("x-grid-split")){
33493             this.cellIndex = this.view.getCellIndex(t.dom);
33494             this.split = t.dom;
33495             this.cm = this.grid.colModel;
33496             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
33497                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
33498             }
33499         }
33500     },
33501
33502     endDrag : function(e){
33503         this.view.headersDisabled = false;
33504         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
33505         var diff = endX - this.startPos;
33506         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
33507     },
33508
33509     autoOffset : function(){
33510         this.setDelta(0,0);
33511     }
33512 });/*
33513  * Based on:
33514  * Ext JS Library 1.1.1
33515  * Copyright(c) 2006-2007, Ext JS, LLC.
33516  *
33517  * Originally Released Under LGPL - original licence link has changed is not relivant.
33518  *
33519  * Fork - LGPL
33520  * <script type="text/javascript">
33521  */
33522  
33523 // private
33524 // This is a support class used internally by the Grid components
33525 Roo.grid.GridDragZone = function(grid, config){
33526     this.view = grid.getView();
33527     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
33528     if(this.view.lockedBody){
33529         this.setHandleElId(Roo.id(this.view.mainBody.dom));
33530         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
33531     }
33532     this.scroll = false;
33533     this.grid = grid;
33534     this.ddel = document.createElement('div');
33535     this.ddel.className = 'x-grid-dd-wrap';
33536 };
33537
33538 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
33539     ddGroup : "GridDD",
33540
33541     getDragData : function(e){
33542         var t = Roo.lib.Event.getTarget(e);
33543         var rowIndex = this.view.findRowIndex(t);
33544         if(rowIndex !== false){
33545             var sm = this.grid.selModel;
33546             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
33547               //  sm.mouseDown(e, t);
33548             //}
33549             if (e.hasModifier()){
33550                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
33551             }
33552             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
33553         }
33554         return false;
33555     },
33556
33557     onInitDrag : function(e){
33558         var data = this.dragData;
33559         this.ddel.innerHTML = this.grid.getDragDropText();
33560         this.proxy.update(this.ddel);
33561         // fire start drag?
33562     },
33563
33564     afterRepair : function(){
33565         this.dragging = false;
33566     },
33567
33568     getRepairXY : function(e, data){
33569         return false;
33570     },
33571
33572     onEndDrag : function(data, e){
33573         // fire end drag?
33574     },
33575
33576     onValidDrop : function(dd, e, id){
33577         // fire drag drop?
33578         this.hideProxy();
33579     },
33580
33581     beforeInvalidDrop : function(e, id){
33582
33583     }
33584 });/*
33585  * Based on:
33586  * Ext JS Library 1.1.1
33587  * Copyright(c) 2006-2007, Ext JS, LLC.
33588  *
33589  * Originally Released Under LGPL - original licence link has changed is not relivant.
33590  *
33591  * Fork - LGPL
33592  * <script type="text/javascript">
33593  */
33594  
33595
33596 /**
33597  * @class Roo.grid.ColumnModel
33598  * @extends Roo.util.Observable
33599  * This is the default implementation of a ColumnModel used by the Grid. It defines
33600  * the columns in the grid.
33601  * <br>Usage:<br>
33602  <pre><code>
33603  var colModel = new Roo.grid.ColumnModel([
33604         {header: "Ticker", width: 60, sortable: true, locked: true},
33605         {header: "Company Name", width: 150, sortable: true},
33606         {header: "Market Cap.", width: 100, sortable: true},
33607         {header: "$ Sales", width: 100, sortable: true, renderer: money},
33608         {header: "Employees", width: 100, sortable: true, resizable: false}
33609  ]);
33610  </code></pre>
33611  * <p>
33612  
33613  * The config options listed for this class are options which may appear in each
33614  * individual column definition.
33615  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
33616  * @constructor
33617  * @param {Object} config An Array of column config objects. See this class's
33618  * config objects for details.
33619 */
33620 Roo.grid.ColumnModel = function(config){
33621         /**
33622      * The config passed into the constructor
33623      */
33624     this.config = config;
33625     this.lookup = {};
33626
33627     // if no id, create one
33628     // if the column does not have a dataIndex mapping,
33629     // map it to the order it is in the config
33630     for(var i = 0, len = config.length; i < len; i++){
33631         var c = config[i];
33632         if(typeof c.dataIndex == "undefined"){
33633             c.dataIndex = i;
33634         }
33635         if(typeof c.renderer == "string"){
33636             c.renderer = Roo.util.Format[c.renderer];
33637         }
33638         if(typeof c.id == "undefined"){
33639             c.id = Roo.id();
33640         }
33641         if(c.editor && c.editor.xtype){
33642             c.editor  = Roo.factory(c.editor, Roo.grid);
33643         }
33644         if(c.editor && c.editor.isFormField){
33645             c.editor = new Roo.grid.GridEditor(c.editor);
33646         }
33647         this.lookup[c.id] = c;
33648     }
33649
33650     /**
33651      * The width of columns which have no width specified (defaults to 100)
33652      * @type Number
33653      */
33654     this.defaultWidth = 100;
33655
33656     /**
33657      * Default sortable of columns which have no sortable specified (defaults to false)
33658      * @type Boolean
33659      */
33660     this.defaultSortable = false;
33661
33662     this.addEvents({
33663         /**
33664              * @event widthchange
33665              * Fires when the width of a column changes.
33666              * @param {ColumnModel} this
33667              * @param {Number} columnIndex The column index
33668              * @param {Number} newWidth The new width
33669              */
33670             "widthchange": true,
33671         /**
33672              * @event headerchange
33673              * Fires when the text of a header changes.
33674              * @param {ColumnModel} this
33675              * @param {Number} columnIndex The column index
33676              * @param {Number} newText The new header text
33677              */
33678             "headerchange": true,
33679         /**
33680              * @event hiddenchange
33681              * Fires when a column is hidden or "unhidden".
33682              * @param {ColumnModel} this
33683              * @param {Number} columnIndex The column index
33684              * @param {Boolean} hidden true if hidden, false otherwise
33685              */
33686             "hiddenchange": true,
33687             /**
33688          * @event columnmoved
33689          * Fires when a column is moved.
33690          * @param {ColumnModel} this
33691          * @param {Number} oldIndex
33692          * @param {Number} newIndex
33693          */
33694         "columnmoved" : true,
33695         /**
33696          * @event columlockchange
33697          * Fires when a column's locked state is changed
33698          * @param {ColumnModel} this
33699          * @param {Number} colIndex
33700          * @param {Boolean} locked true if locked
33701          */
33702         "columnlockchange" : true
33703     });
33704     Roo.grid.ColumnModel.superclass.constructor.call(this);
33705 };
33706 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
33707     /**
33708      * @cfg {String} header The header text to display in the Grid view.
33709      */
33710     /**
33711      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
33712      * {@link Roo.data.Record} definition from which to draw the column's value. If not
33713      * specified, the column's index is used as an index into the Record's data Array.
33714      */
33715     /**
33716      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
33717      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
33718      */
33719     /**
33720      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
33721      * Defaults to the value of the {@link #defaultSortable} property.
33722      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
33723      */
33724     /**
33725      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
33726      */
33727     /**
33728      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
33729      */
33730     /**
33731      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
33732      */
33733     /**
33734      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
33735      */
33736     /**
33737      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
33738      * given the cell's data value. See {@link #setRenderer}. If not specified, the
33739      * default renderer uses the raw data value.
33740      */
33741        /**
33742      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
33743      */
33744     /**
33745      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
33746      */
33747
33748     /**
33749      * Returns the id of the column at the specified index.
33750      * @param {Number} index The column index
33751      * @return {String} the id
33752      */
33753     getColumnId : function(index){
33754         return this.config[index].id;
33755     },
33756
33757     /**
33758      * Returns the column for a specified id.
33759      * @param {String} id The column id
33760      * @return {Object} the column
33761      */
33762     getColumnById : function(id){
33763         return this.lookup[id];
33764     },
33765
33766     
33767     /**
33768      * Returns the column for a specified dataIndex.
33769      * @param {String} dataIndex The column dataIndex
33770      * @return {Object|Boolean} the column or false if not found
33771      */
33772     getColumnByDataIndex: function(dataIndex){
33773         var index = this.findColumnIndex(dataIndex);
33774         return ci > -1 ? this.his.config[index] : false;
33775     },
33776     
33777     /**
33778      * Returns the index for a specified column id.
33779      * @param {String} id The column id
33780      * @return {Number} the index, or -1 if not found
33781      */
33782     getIndexById : function(id){
33783         for(var i = 0, len = this.config.length; i < len; i++){
33784             if(this.config[i].id == id){
33785                 return i;
33786             }
33787         }
33788         return -1;
33789     },
33790     
33791     /**
33792      * Returns the index for a specified column dataIndex.
33793      * @param {String} dataIndex The column dataIndex
33794      * @return {Number} the index, or -1 if not found
33795      */
33796     
33797     findColumnIndex : function(dataIndex){
33798         for(var i = 0, len = this.config.length; i < len; i++){
33799             if(this.config[i].dataIndex == dataIndex){
33800                 return i;
33801             }
33802         }
33803         return -1;
33804     },
33805     
33806     
33807     moveColumn : function(oldIndex, newIndex){
33808         var c = this.config[oldIndex];
33809         this.config.splice(oldIndex, 1);
33810         this.config.splice(newIndex, 0, c);
33811         this.dataMap = null;
33812         this.fireEvent("columnmoved", this, oldIndex, newIndex);
33813     },
33814
33815     isLocked : function(colIndex){
33816         return this.config[colIndex].locked === true;
33817     },
33818
33819     setLocked : function(colIndex, value, suppressEvent){
33820         if(this.isLocked(colIndex) == value){
33821             return;
33822         }
33823         this.config[colIndex].locked = value;
33824         if(!suppressEvent){
33825             this.fireEvent("columnlockchange", this, colIndex, value);
33826         }
33827     },
33828
33829     getTotalLockedWidth : function(){
33830         var totalWidth = 0;
33831         for(var i = 0; i < this.config.length; i++){
33832             if(this.isLocked(i) && !this.isHidden(i)){
33833                 this.totalWidth += this.getColumnWidth(i);
33834             }
33835         }
33836         return totalWidth;
33837     },
33838
33839     getLockedCount : function(){
33840         for(var i = 0, len = this.config.length; i < len; i++){
33841             if(!this.isLocked(i)){
33842                 return i;
33843             }
33844         }
33845     },
33846
33847     /**
33848      * Returns the number of columns.
33849      * @return {Number}
33850      */
33851     getColumnCount : function(visibleOnly){
33852         if(visibleOnly === true){
33853             var c = 0;
33854             for(var i = 0, len = this.config.length; i < len; i++){
33855                 if(!this.isHidden(i)){
33856                     c++;
33857                 }
33858             }
33859             return c;
33860         }
33861         return this.config.length;
33862     },
33863
33864     /**
33865      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
33866      * @param {Function} fn
33867      * @param {Object} scope (optional)
33868      * @return {Array} result
33869      */
33870     getColumnsBy : function(fn, scope){
33871         var r = [];
33872         for(var i = 0, len = this.config.length; i < len; i++){
33873             var c = this.config[i];
33874             if(fn.call(scope||this, c, i) === true){
33875                 r[r.length] = c;
33876             }
33877         }
33878         return r;
33879     },
33880
33881     /**
33882      * Returns true if the specified column is sortable.
33883      * @param {Number} col The column index
33884      * @return {Boolean}
33885      */
33886     isSortable : function(col){
33887         if(typeof this.config[col].sortable == "undefined"){
33888             return this.defaultSortable;
33889         }
33890         return this.config[col].sortable;
33891     },
33892
33893     /**
33894      * Returns the rendering (formatting) function defined for the column.
33895      * @param {Number} col The column index.
33896      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
33897      */
33898     getRenderer : function(col){
33899         if(!this.config[col].renderer){
33900             return Roo.grid.ColumnModel.defaultRenderer;
33901         }
33902         return this.config[col].renderer;
33903     },
33904
33905     /**
33906      * Sets the rendering (formatting) function for a column.
33907      * @param {Number} col The column index
33908      * @param {Function} fn The function to use to process the cell's raw data
33909      * to return HTML markup for the grid view. The render function is called with
33910      * the following parameters:<ul>
33911      * <li>Data value.</li>
33912      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
33913      * <li>css A CSS style string to apply to the table cell.</li>
33914      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
33915      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
33916      * <li>Row index</li>
33917      * <li>Column index</li>
33918      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
33919      */
33920     setRenderer : function(col, fn){
33921         this.config[col].renderer = fn;
33922     },
33923
33924     /**
33925      * Returns the width for the specified column.
33926      * @param {Number} col The column index
33927      * @return {Number}
33928      */
33929     getColumnWidth : function(col){
33930         return this.config[col].width || this.defaultWidth;
33931     },
33932
33933     /**
33934      * Sets the width for a column.
33935      * @param {Number} col The column index
33936      * @param {Number} width The new width
33937      */
33938     setColumnWidth : function(col, width, suppressEvent){
33939         this.config[col].width = width;
33940         this.totalWidth = null;
33941         if(!suppressEvent){
33942              this.fireEvent("widthchange", this, col, width);
33943         }
33944     },
33945
33946     /**
33947      * Returns the total width of all columns.
33948      * @param {Boolean} includeHidden True to include hidden column widths
33949      * @return {Number}
33950      */
33951     getTotalWidth : function(includeHidden){
33952         if(!this.totalWidth){
33953             this.totalWidth = 0;
33954             for(var i = 0, len = this.config.length; i < len; i++){
33955                 if(includeHidden || !this.isHidden(i)){
33956                     this.totalWidth += this.getColumnWidth(i);
33957                 }
33958             }
33959         }
33960         return this.totalWidth;
33961     },
33962
33963     /**
33964      * Returns the header for the specified column.
33965      * @param {Number} col The column index
33966      * @return {String}
33967      */
33968     getColumnHeader : function(col){
33969         return this.config[col].header;
33970     },
33971
33972     /**
33973      * Sets the header for a column.
33974      * @param {Number} col The column index
33975      * @param {String} header The new header
33976      */
33977     setColumnHeader : function(col, header){
33978         this.config[col].header = header;
33979         this.fireEvent("headerchange", this, col, header);
33980     },
33981
33982     /**
33983      * Returns the tooltip for the specified column.
33984      * @param {Number} col The column index
33985      * @return {String}
33986      */
33987     getColumnTooltip : function(col){
33988             return this.config[col].tooltip;
33989     },
33990     /**
33991      * Sets the tooltip for a column.
33992      * @param {Number} col The column index
33993      * @param {String} tooltip The new tooltip
33994      */
33995     setColumnTooltip : function(col, tooltip){
33996             this.config[col].tooltip = tooltip;
33997     },
33998
33999     /**
34000      * Returns the dataIndex for the specified column.
34001      * @param {Number} col The column index
34002      * @return {Number}
34003      */
34004     getDataIndex : function(col){
34005         return this.config[col].dataIndex;
34006     },
34007
34008     /**
34009      * Sets the dataIndex for a column.
34010      * @param {Number} col The column index
34011      * @param {Number} dataIndex The new dataIndex
34012      */
34013     setDataIndex : function(col, dataIndex){
34014         this.config[col].dataIndex = dataIndex;
34015     },
34016
34017     
34018     
34019     /**
34020      * Returns true if the cell is editable.
34021      * @param {Number} colIndex The column index
34022      * @param {Number} rowIndex The row index
34023      * @return {Boolean}
34024      */
34025     isCellEditable : function(colIndex, rowIndex){
34026         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34027     },
34028
34029     /**
34030      * Returns the editor defined for the cell/column.
34031      * return false or null to disable editing.
34032      * @param {Number} colIndex The column index
34033      * @param {Number} rowIndex The row index
34034      * @return {Object}
34035      */
34036     getCellEditor : function(colIndex, rowIndex){
34037         return this.config[colIndex].editor;
34038     },
34039
34040     /**
34041      * Sets if a column is editable.
34042      * @param {Number} col The column index
34043      * @param {Boolean} editable True if the column is editable
34044      */
34045     setEditable : function(col, editable){
34046         this.config[col].editable = editable;
34047     },
34048
34049
34050     /**
34051      * Returns true if the column is hidden.
34052      * @param {Number} colIndex The column index
34053      * @return {Boolean}
34054      */
34055     isHidden : function(colIndex){
34056         return this.config[colIndex].hidden;
34057     },
34058
34059
34060     /**
34061      * Returns true if the column width cannot be changed
34062      */
34063     isFixed : function(colIndex){
34064         return this.config[colIndex].fixed;
34065     },
34066
34067     /**
34068      * Returns true if the column can be resized
34069      * @return {Boolean}
34070      */
34071     isResizable : function(colIndex){
34072         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34073     },
34074     /**
34075      * Sets if a column is hidden.
34076      * @param {Number} colIndex The column index
34077      * @param {Boolean} hidden True if the column is hidden
34078      */
34079     setHidden : function(colIndex, hidden){
34080         this.config[colIndex].hidden = hidden;
34081         this.totalWidth = null;
34082         this.fireEvent("hiddenchange", this, colIndex, hidden);
34083     },
34084
34085     /**
34086      * Sets the editor for a column.
34087      * @param {Number} col The column index
34088      * @param {Object} editor The editor object
34089      */
34090     setEditor : function(col, editor){
34091         this.config[col].editor = editor;
34092     }
34093 });
34094
34095 Roo.grid.ColumnModel.defaultRenderer = function(value){
34096         if(typeof value == "string" && value.length < 1){
34097             return "&#160;";
34098         }
34099         return value;
34100 };
34101
34102 // Alias for backwards compatibility
34103 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34104 /*
34105  * Based on:
34106  * Ext JS Library 1.1.1
34107  * Copyright(c) 2006-2007, Ext JS, LLC.
34108  *
34109  * Originally Released Under LGPL - original licence link has changed is not relivant.
34110  *
34111  * Fork - LGPL
34112  * <script type="text/javascript">
34113  */
34114
34115 /**
34116  * @class Roo.grid.AbstractSelectionModel
34117  * @extends Roo.util.Observable
34118  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34119  * implemented by descendant classes.  This class should not be directly instantiated.
34120  * @constructor
34121  */
34122 Roo.grid.AbstractSelectionModel = function(){
34123     this.locked = false;
34124     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34125 };
34126
34127 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34128     /** @ignore Called by the grid automatically. Do not call directly. */
34129     init : function(grid){
34130         this.grid = grid;
34131         this.initEvents();
34132     },
34133
34134     /**
34135      * Locks the selections.
34136      */
34137     lock : function(){
34138         this.locked = true;
34139     },
34140
34141     /**
34142      * Unlocks the selections.
34143      */
34144     unlock : function(){
34145         this.locked = false;
34146     },
34147
34148     /**
34149      * Returns true if the selections are locked.
34150      * @return {Boolean}
34151      */
34152     isLocked : function(){
34153         return this.locked;
34154     }
34155 });/*
34156  * Based on:
34157  * Ext JS Library 1.1.1
34158  * Copyright(c) 2006-2007, Ext JS, LLC.
34159  *
34160  * Originally Released Under LGPL - original licence link has changed is not relivant.
34161  *
34162  * Fork - LGPL
34163  * <script type="text/javascript">
34164  */
34165 /**
34166  * @extends Roo.grid.AbstractSelectionModel
34167  * @class Roo.grid.RowSelectionModel
34168  * The default SelectionModel used by {@link Roo.grid.Grid}.
34169  * It supports multiple selections and keyboard selection/navigation. 
34170  * @constructor
34171  * @param {Object} config
34172  */
34173 Roo.grid.RowSelectionModel = function(config){
34174     Roo.apply(this, config);
34175     this.selections = new Roo.util.MixedCollection(false, function(o){
34176         return o.id;
34177     });
34178
34179     this.last = false;
34180     this.lastActive = false;
34181
34182     this.addEvents({
34183         /**
34184              * @event selectionchange
34185              * Fires when the selection changes
34186              * @param {SelectionModel} this
34187              */
34188             "selectionchange" : true,
34189         /**
34190              * @event afterselectionchange
34191              * Fires after the selection changes (eg. by key press or clicking)
34192              * @param {SelectionModel} this
34193              */
34194             "afterselectionchange" : true,
34195         /**
34196              * @event beforerowselect
34197              * Fires when a row is selected being selected, return false to cancel.
34198              * @param {SelectionModel} this
34199              * @param {Number} rowIndex The selected index
34200              * @param {Boolean} keepExisting False if other selections will be cleared
34201              */
34202             "beforerowselect" : true,
34203         /**
34204              * @event rowselect
34205              * Fires when a row is selected.
34206              * @param {SelectionModel} this
34207              * @param {Number} rowIndex The selected index
34208              * @param {Roo.data.Record} r The record
34209              */
34210             "rowselect" : true,
34211         /**
34212              * @event rowdeselect
34213              * Fires when a row is deselected.
34214              * @param {SelectionModel} this
34215              * @param {Number} rowIndex The selected index
34216              */
34217         "rowdeselect" : true
34218     });
34219     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
34220     this.locked = false;
34221 };
34222
34223 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
34224     /**
34225      * @cfg {Boolean} singleSelect
34226      * True to allow selection of only one row at a time (defaults to false)
34227      */
34228     singleSelect : false,
34229
34230     // private
34231     initEvents : function(){
34232
34233         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
34234             this.grid.on("mousedown", this.handleMouseDown, this);
34235         }else{ // allow click to work like normal
34236             this.grid.on("rowclick", this.handleDragableRowClick, this);
34237         }
34238
34239         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
34240             "up" : function(e){
34241                 if(!e.shiftKey){
34242                     this.selectPrevious(e.shiftKey);
34243                 }else if(this.last !== false && this.lastActive !== false){
34244                     var last = this.last;
34245                     this.selectRange(this.last,  this.lastActive-1);
34246                     this.grid.getView().focusRow(this.lastActive);
34247                     if(last !== false){
34248                         this.last = last;
34249                     }
34250                 }else{
34251                     this.selectFirstRow();
34252                 }
34253                 this.fireEvent("afterselectionchange", this);
34254             },
34255             "down" : function(e){
34256                 if(!e.shiftKey){
34257                     this.selectNext(e.shiftKey);
34258                 }else if(this.last !== false && this.lastActive !== false){
34259                     var last = this.last;
34260                     this.selectRange(this.last,  this.lastActive+1);
34261                     this.grid.getView().focusRow(this.lastActive);
34262                     if(last !== false){
34263                         this.last = last;
34264                     }
34265                 }else{
34266                     this.selectFirstRow();
34267                 }
34268                 this.fireEvent("afterselectionchange", this);
34269             },
34270             scope: this
34271         });
34272
34273         var view = this.grid.view;
34274         view.on("refresh", this.onRefresh, this);
34275         view.on("rowupdated", this.onRowUpdated, this);
34276         view.on("rowremoved", this.onRemove, this);
34277     },
34278
34279     // private
34280     onRefresh : function(){
34281         var ds = this.grid.dataSource, i, v = this.grid.view;
34282         var s = this.selections;
34283         s.each(function(r){
34284             if((i = ds.indexOfId(r.id)) != -1){
34285                 v.onRowSelect(i);
34286             }else{
34287                 s.remove(r);
34288             }
34289         });
34290     },
34291
34292     // private
34293     onRemove : function(v, index, r){
34294         this.selections.remove(r);
34295     },
34296
34297     // private
34298     onRowUpdated : function(v, index, r){
34299         if(this.isSelected(r)){
34300             v.onRowSelect(index);
34301         }
34302     },
34303
34304     /**
34305      * Select records.
34306      * @param {Array} records The records to select
34307      * @param {Boolean} keepExisting (optional) True to keep existing selections
34308      */
34309     selectRecords : function(records, keepExisting){
34310         if(!keepExisting){
34311             this.clearSelections();
34312         }
34313         var ds = this.grid.dataSource;
34314         for(var i = 0, len = records.length; i < len; i++){
34315             this.selectRow(ds.indexOf(records[i]), true);
34316         }
34317     },
34318
34319     /**
34320      * Gets the number of selected rows.
34321      * @return {Number}
34322      */
34323     getCount : function(){
34324         return this.selections.length;
34325     },
34326
34327     /**
34328      * Selects the first row in the grid.
34329      */
34330     selectFirstRow : function(){
34331         this.selectRow(0);
34332     },
34333
34334     /**
34335      * Select the last row.
34336      * @param {Boolean} keepExisting (optional) True to keep existing selections
34337      */
34338     selectLastRow : function(keepExisting){
34339         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
34340     },
34341
34342     /**
34343      * Selects the row immediately following the last selected row.
34344      * @param {Boolean} keepExisting (optional) True to keep existing selections
34345      */
34346     selectNext : function(keepExisting){
34347         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
34348             this.selectRow(this.last+1, keepExisting);
34349             this.grid.getView().focusRow(this.last);
34350         }
34351     },
34352
34353     /**
34354      * Selects the row that precedes the last selected row.
34355      * @param {Boolean} keepExisting (optional) True to keep existing selections
34356      */
34357     selectPrevious : function(keepExisting){
34358         if(this.last){
34359             this.selectRow(this.last-1, keepExisting);
34360             this.grid.getView().focusRow(this.last);
34361         }
34362     },
34363
34364     /**
34365      * Returns the selected records
34366      * @return {Array} Array of selected records
34367      */
34368     getSelections : function(){
34369         return [].concat(this.selections.items);
34370     },
34371
34372     /**
34373      * Returns the first selected record.
34374      * @return {Record}
34375      */
34376     getSelected : function(){
34377         return this.selections.itemAt(0);
34378     },
34379
34380
34381     /**
34382      * Clears all selections.
34383      */
34384     clearSelections : function(fast){
34385         if(this.locked) return;
34386         if(fast !== true){
34387             var ds = this.grid.dataSource;
34388             var s = this.selections;
34389             s.each(function(r){
34390                 this.deselectRow(ds.indexOfId(r.id));
34391             }, this);
34392             s.clear();
34393         }else{
34394             this.selections.clear();
34395         }
34396         this.last = false;
34397     },
34398
34399
34400     /**
34401      * Selects all rows.
34402      */
34403     selectAll : function(){
34404         if(this.locked) return;
34405         this.selections.clear();
34406         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
34407             this.selectRow(i, true);
34408         }
34409     },
34410
34411     /**
34412      * Returns True if there is a selection.
34413      * @return {Boolean}
34414      */
34415     hasSelection : function(){
34416         return this.selections.length > 0;
34417     },
34418
34419     /**
34420      * Returns True if the specified row is selected.
34421      * @param {Number/Record} record The record or index of the record to check
34422      * @return {Boolean}
34423      */
34424     isSelected : function(index){
34425         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
34426         return (r && this.selections.key(r.id) ? true : false);
34427     },
34428
34429     /**
34430      * Returns True if the specified record id is selected.
34431      * @param {String} id The id of record to check
34432      * @return {Boolean}
34433      */
34434     isIdSelected : function(id){
34435         return (this.selections.key(id) ? true : false);
34436     },
34437
34438     // private
34439     handleMouseDown : function(e, t){
34440         var view = this.grid.getView(), rowIndex;
34441         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
34442             return;
34443         };
34444         if(e.shiftKey && this.last !== false){
34445             var last = this.last;
34446             this.selectRange(last, rowIndex, e.ctrlKey);
34447             this.last = last; // reset the last
34448             view.focusRow(rowIndex);
34449         }else{
34450             var isSelected = this.isSelected(rowIndex);
34451             if(e.button !== 0 && isSelected){
34452                 view.focusRow(rowIndex);
34453             }else if(e.ctrlKey && isSelected){
34454                 this.deselectRow(rowIndex);
34455             }else if(!isSelected){
34456                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
34457                 view.focusRow(rowIndex);
34458             }
34459         }
34460         this.fireEvent("afterselectionchange", this);
34461     },
34462     // private
34463     handleDragableRowClick :  function(grid, rowIndex, e) 
34464     {
34465         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
34466             this.selectRow(rowIndex, false);
34467             grid.view.focusRow(rowIndex);
34468              this.fireEvent("afterselectionchange", this);
34469         }
34470     },
34471     
34472     /**
34473      * Selects multiple rows.
34474      * @param {Array} rows Array of the indexes of the row to select
34475      * @param {Boolean} keepExisting (optional) True to keep existing selections
34476      */
34477     selectRows : function(rows, keepExisting){
34478         if(!keepExisting){
34479             this.clearSelections();
34480         }
34481         for(var i = 0, len = rows.length; i < len; i++){
34482             this.selectRow(rows[i], true);
34483         }
34484     },
34485
34486     /**
34487      * Selects a range of rows. All rows in between startRow and endRow are also selected.
34488      * @param {Number} startRow The index of the first row in the range
34489      * @param {Number} endRow The index of the last row in the range
34490      * @param {Boolean} keepExisting (optional) True to retain existing selections
34491      */
34492     selectRange : function(startRow, endRow, keepExisting){
34493         if(this.locked) return;
34494         if(!keepExisting){
34495             this.clearSelections();
34496         }
34497         if(startRow <= endRow){
34498             for(var i = startRow; i <= endRow; i++){
34499                 this.selectRow(i, true);
34500             }
34501         }else{
34502             for(var i = startRow; i >= endRow; i--){
34503                 this.selectRow(i, true);
34504             }
34505         }
34506     },
34507
34508     /**
34509      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
34510      * @param {Number} startRow The index of the first row in the range
34511      * @param {Number} endRow The index of the last row in the range
34512      */
34513     deselectRange : function(startRow, endRow, preventViewNotify){
34514         if(this.locked) return;
34515         for(var i = startRow; i <= endRow; i++){
34516             this.deselectRow(i, preventViewNotify);
34517         }
34518     },
34519
34520     /**
34521      * Selects a row.
34522      * @param {Number} row The index of the row to select
34523      * @param {Boolean} keepExisting (optional) True to keep existing selections
34524      */
34525     selectRow : function(index, keepExisting, preventViewNotify){
34526         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
34527         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
34528             if(!keepExisting || this.singleSelect){
34529                 this.clearSelections();
34530             }
34531             var r = this.grid.dataSource.getAt(index);
34532             this.selections.add(r);
34533             this.last = this.lastActive = index;
34534             if(!preventViewNotify){
34535                 this.grid.getView().onRowSelect(index);
34536             }
34537             this.fireEvent("rowselect", this, index, r);
34538             this.fireEvent("selectionchange", this);
34539         }
34540     },
34541
34542     /**
34543      * Deselects a row.
34544      * @param {Number} row The index of the row to deselect
34545      */
34546     deselectRow : function(index, preventViewNotify){
34547         if(this.locked) return;
34548         if(this.last == index){
34549             this.last = false;
34550         }
34551         if(this.lastActive == index){
34552             this.lastActive = false;
34553         }
34554         var r = this.grid.dataSource.getAt(index);
34555         this.selections.remove(r);
34556         if(!preventViewNotify){
34557             this.grid.getView().onRowDeselect(index);
34558         }
34559         this.fireEvent("rowdeselect", this, index);
34560         this.fireEvent("selectionchange", this);
34561     },
34562
34563     // private
34564     restoreLast : function(){
34565         if(this._last){
34566             this.last = this._last;
34567         }
34568     },
34569
34570     // private
34571     acceptsNav : function(row, col, cm){
34572         return !cm.isHidden(col) && cm.isCellEditable(col, row);
34573     },
34574
34575     // private
34576     onEditorKey : function(field, e){
34577         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
34578         if(k == e.TAB){
34579             e.stopEvent();
34580             ed.completeEdit();
34581             if(e.shiftKey){
34582                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
34583             }else{
34584                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
34585             }
34586         }else if(k == e.ENTER && !e.ctrlKey){
34587             e.stopEvent();
34588             ed.completeEdit();
34589             if(e.shiftKey){
34590                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
34591             }else{
34592                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
34593             }
34594         }else if(k == e.ESC){
34595             ed.cancelEdit();
34596         }
34597         if(newCell){
34598             g.startEditing(newCell[0], newCell[1]);
34599         }
34600     }
34601 });/*
34602  * Based on:
34603  * Ext JS Library 1.1.1
34604  * Copyright(c) 2006-2007, Ext JS, LLC.
34605  *
34606  * Originally Released Under LGPL - original licence link has changed is not relivant.
34607  *
34608  * Fork - LGPL
34609  * <script type="text/javascript">
34610  */
34611 /**
34612  * @class Roo.grid.CellSelectionModel
34613  * @extends Roo.grid.AbstractSelectionModel
34614  * This class provides the basic implementation for cell selection in a grid.
34615  * @constructor
34616  * @param {Object} config The object containing the configuration of this model.
34617  */
34618 Roo.grid.CellSelectionModel = function(config){
34619     Roo.apply(this, config);
34620
34621     this.selection = null;
34622
34623     this.addEvents({
34624         /**
34625              * @event beforerowselect
34626              * Fires before a cell is selected.
34627              * @param {SelectionModel} this
34628              * @param {Number} rowIndex The selected row index
34629              * @param {Number} colIndex The selected cell index
34630              */
34631             "beforecellselect" : true,
34632         /**
34633              * @event cellselect
34634              * Fires when a cell is selected.
34635              * @param {SelectionModel} this
34636              * @param {Number} rowIndex The selected row index
34637              * @param {Number} colIndex The selected cell index
34638              */
34639             "cellselect" : true,
34640         /**
34641              * @event selectionchange
34642              * Fires when the active selection changes.
34643              * @param {SelectionModel} this
34644              * @param {Object} selection null for no selection or an object (o) with two properties
34645                 <ul>
34646                 <li>o.record: the record object for the row the selection is in</li>
34647                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
34648                 </ul>
34649              */
34650             "selectionchange" : true
34651     });
34652     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
34653 };
34654
34655 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
34656
34657     /** @ignore */
34658     initEvents : function(){
34659         this.grid.on("mousedown", this.handleMouseDown, this);
34660         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
34661         var view = this.grid.view;
34662         view.on("refresh", this.onViewChange, this);
34663         view.on("rowupdated", this.onRowUpdated, this);
34664         view.on("beforerowremoved", this.clearSelections, this);
34665         view.on("beforerowsinserted", this.clearSelections, this);
34666         if(this.grid.isEditor){
34667             this.grid.on("beforeedit", this.beforeEdit,  this);
34668         }
34669     },
34670
34671         //private
34672     beforeEdit : function(e){
34673         this.select(e.row, e.column, false, true, e.record);
34674     },
34675
34676         //private
34677     onRowUpdated : function(v, index, r){
34678         if(this.selection && this.selection.record == r){
34679             v.onCellSelect(index, this.selection.cell[1]);
34680         }
34681     },
34682
34683         //private
34684     onViewChange : function(){
34685         this.clearSelections(true);
34686     },
34687
34688         /**
34689          * Returns the currently selected cell,.
34690          * @return {Array} The selected cell (row, column) or null if none selected.
34691          */
34692     getSelectedCell : function(){
34693         return this.selection ? this.selection.cell : null;
34694     },
34695
34696     /**
34697      * Clears all selections.
34698      * @param {Boolean} true to prevent the gridview from being notified about the change.
34699      */
34700     clearSelections : function(preventNotify){
34701         var s = this.selection;
34702         if(s){
34703             if(preventNotify !== true){
34704                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
34705             }
34706             this.selection = null;
34707             this.fireEvent("selectionchange", this, null);
34708         }
34709     },
34710
34711     /**
34712      * Returns true if there is a selection.
34713      * @return {Boolean}
34714      */
34715     hasSelection : function(){
34716         return this.selection ? true : false;
34717     },
34718
34719     /** @ignore */
34720     handleMouseDown : function(e, t){
34721         var v = this.grid.getView();
34722         if(this.isLocked()){
34723             return;
34724         };
34725         var row = v.findRowIndex(t);
34726         var cell = v.findCellIndex(t);
34727         if(row !== false && cell !== false){
34728             this.select(row, cell);
34729         }
34730     },
34731
34732     /**
34733      * Selects a cell.
34734      * @param {Number} rowIndex
34735      * @param {Number} collIndex
34736      */
34737     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
34738         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
34739             this.clearSelections();
34740             r = r || this.grid.dataSource.getAt(rowIndex);
34741             this.selection = {
34742                 record : r,
34743                 cell : [rowIndex, colIndex]
34744             };
34745             if(!preventViewNotify){
34746                 var v = this.grid.getView();
34747                 v.onCellSelect(rowIndex, colIndex);
34748                 if(preventFocus !== true){
34749                     v.focusCell(rowIndex, colIndex);
34750                 }
34751             }
34752             this.fireEvent("cellselect", this, rowIndex, colIndex);
34753             this.fireEvent("selectionchange", this, this.selection);
34754         }
34755     },
34756
34757         //private
34758     isSelectable : function(rowIndex, colIndex, cm){
34759         return !cm.isHidden(colIndex);
34760     },
34761
34762     /** @ignore */
34763     handleKeyDown : function(e){
34764         if(!e.isNavKeyPress()){
34765             return;
34766         }
34767         var g = this.grid, s = this.selection;
34768         if(!s){
34769             e.stopEvent();
34770             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
34771             if(cell){
34772                 this.select(cell[0], cell[1]);
34773             }
34774             return;
34775         }
34776         var sm = this;
34777         var walk = function(row, col, step){
34778             return g.walkCells(row, col, step, sm.isSelectable,  sm);
34779         };
34780         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
34781         var newCell;
34782
34783         switch(k){
34784              case e.TAB:
34785                  if(e.shiftKey){
34786                      newCell = walk(r, c-1, -1);
34787                  }else{
34788                      newCell = walk(r, c+1, 1);
34789                  }
34790              break;
34791              case e.DOWN:
34792                  newCell = walk(r+1, c, 1);
34793              break;
34794              case e.UP:
34795                  newCell = walk(r-1, c, -1);
34796              break;
34797              case e.RIGHT:
34798                  newCell = walk(r, c+1, 1);
34799              break;
34800              case e.LEFT:
34801                  newCell = walk(r, c-1, -1);
34802              break;
34803              case e.ENTER:
34804                  if(g.isEditor && !g.editing){
34805                     g.startEditing(r, c);
34806                     e.stopEvent();
34807                     return;
34808                 }
34809              break;
34810         };
34811         if(newCell){
34812             this.select(newCell[0], newCell[1]);
34813             e.stopEvent();
34814         }
34815     },
34816
34817     acceptsNav : function(row, col, cm){
34818         return !cm.isHidden(col) && cm.isCellEditable(col, row);
34819     },
34820
34821     onEditorKey : function(field, e){
34822         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
34823         if(k == e.TAB){
34824             if(e.shiftKey){
34825                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
34826             }else{
34827                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
34828             }
34829             e.stopEvent();
34830         }else if(k == e.ENTER && !e.ctrlKey){
34831             ed.completeEdit();
34832             e.stopEvent();
34833         }else if(k == e.ESC){
34834             ed.cancelEdit();
34835         }
34836         if(newCell){
34837             g.startEditing(newCell[0], newCell[1]);
34838         }
34839     }
34840 });/*
34841  * Based on:
34842  * Ext JS Library 1.1.1
34843  * Copyright(c) 2006-2007, Ext JS, LLC.
34844  *
34845  * Originally Released Under LGPL - original licence link has changed is not relivant.
34846  *
34847  * Fork - LGPL
34848  * <script type="text/javascript">
34849  */
34850  
34851 /**
34852  * @class Roo.grid.EditorGrid
34853  * @extends Roo.grid.Grid
34854  * Class for creating and editable grid.
34855  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
34856  * The container MUST have some type of size defined for the grid to fill. The container will be 
34857  * automatically set to position relative if it isn't already.
34858  * @param {Object} dataSource The data model to bind to
34859  * @param {Object} colModel The column model with info about this grid's columns
34860  */
34861 Roo.grid.EditorGrid = function(container, config){
34862     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
34863     this.getGridEl().addClass("xedit-grid");
34864
34865     if(!this.selModel){
34866         this.selModel = new Roo.grid.CellSelectionModel();
34867     }
34868
34869     this.activeEditor = null;
34870
34871         this.addEvents({
34872             /**
34873              * @event beforeedit
34874              * Fires before cell editing is triggered. The edit event object has the following properties <br />
34875              * <ul style="padding:5px;padding-left:16px;">
34876              * <li>grid - This grid</li>
34877              * <li>record - The record being edited</li>
34878              * <li>field - The field name being edited</li>
34879              * <li>value - The value for the field being edited.</li>
34880              * <li>row - The grid row index</li>
34881              * <li>column - The grid column index</li>
34882              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
34883              * </ul>
34884              * @param {Object} e An edit event (see above for description)
34885              */
34886             "beforeedit" : true,
34887             /**
34888              * @event afteredit
34889              * Fires after a cell is edited. <br />
34890              * <ul style="padding:5px;padding-left:16px;">
34891              * <li>grid - This grid</li>
34892              * <li>record - The record being edited</li>
34893              * <li>field - The field name being edited</li>
34894              * <li>value - The value being set</li>
34895              * <li>originalValue - The original value for the field, before the edit.</li>
34896              * <li>row - The grid row index</li>
34897              * <li>column - The grid column index</li>
34898              * </ul>
34899              * @param {Object} e An edit event (see above for description)
34900              */
34901             "afteredit" : true,
34902             /**
34903              * @event validateedit
34904              * Fires after a cell is edited, but before the value is set in the record. 
34905          * You can use this to modify the value being set in the field, Return false
34906              * to cancel the change. The edit event object has the following properties <br />
34907              * <ul style="padding:5px;padding-left:16px;">
34908          * <li>editor - This editor</li>
34909              * <li>grid - This grid</li>
34910              * <li>record - The record being edited</li>
34911              * <li>field - The field name being edited</li>
34912              * <li>value - The value being set</li>
34913              * <li>originalValue - The original value for the field, before the edit.</li>
34914              * <li>row - The grid row index</li>
34915              * <li>column - The grid column index</li>
34916              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
34917              * </ul>
34918              * @param {Object} e An edit event (see above for description)
34919              */
34920             "validateedit" : true
34921         });
34922     this.on("bodyscroll", this.stopEditing,  this);
34923     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
34924 };
34925
34926 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
34927     /**
34928      * @cfg {Number} clicksToEdit
34929      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
34930      */
34931     clicksToEdit: 2,
34932
34933     // private
34934     isEditor : true,
34935     // private
34936     trackMouseOver: false, // causes very odd FF errors
34937
34938     onCellDblClick : function(g, row, col){
34939         this.startEditing(row, col);
34940     },
34941
34942     onEditComplete : function(ed, value, startValue){
34943         this.editing = false;
34944         this.activeEditor = null;
34945         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
34946         var r = ed.record;
34947         var field = this.colModel.getDataIndex(ed.col);
34948         var e = {
34949             grid: this,
34950             record: r,
34951             field: field,
34952             originalValue: startValue,
34953             value: value,
34954             row: ed.row,
34955             column: ed.col,
34956             cancel:false,
34957             editor: ed
34958         };
34959         if(String(value) !== String(startValue)){
34960             
34961             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
34962                 r.set(field, e.value);
34963                 delete e.cancel; //?? why!!!
34964                 this.fireEvent("afteredit", e);
34965             }
34966         } else {
34967             this.fireEvent("afteredit", e); // always fir it!
34968         }
34969         this.view.focusCell(ed.row, ed.col);
34970     },
34971
34972     /**
34973      * Starts editing the specified for the specified row/column
34974      * @param {Number} rowIndex
34975      * @param {Number} colIndex
34976      */
34977     startEditing : function(row, col){
34978         this.stopEditing();
34979         if(this.colModel.isCellEditable(col, row)){
34980             this.view.ensureVisible(row, col, true);
34981             var r = this.dataSource.getAt(row);
34982             var field = this.colModel.getDataIndex(col);
34983             var e = {
34984                 grid: this,
34985                 record: r,
34986                 field: field,
34987                 value: r.data[field],
34988                 row: row,
34989                 column: col,
34990                 cancel:false
34991             };
34992             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
34993                 this.editing = true;
34994                 var ed = this.colModel.getCellEditor(col, row);
34995                 
34996                 if (!ed) {
34997                     return;
34998                 }
34999                 if(!ed.rendered){
35000                     ed.render(ed.parentEl || document.body);
35001                 }
35002                 ed.field.reset();
35003                 (function(){ // complex but required for focus issues in safari, ie and opera
35004                     ed.row = row;
35005                     ed.col = col;
35006                     ed.record = r;
35007                     ed.on("complete", this.onEditComplete, this, {single: true});
35008                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35009                     this.activeEditor = ed;
35010                     var v = r.data[field];
35011                     ed.startEdit(this.view.getCell(row, col), v);
35012                 }).defer(50, this);
35013             }
35014         }
35015     },
35016         
35017     /**
35018      * Stops any active editing
35019      */
35020     stopEditing : function(){
35021         if(this.activeEditor){
35022             this.activeEditor.completeEdit();
35023         }
35024         this.activeEditor = null;
35025     }
35026 });/*
35027  * Based on:
35028  * Ext JS Library 1.1.1
35029  * Copyright(c) 2006-2007, Ext JS, LLC.
35030  *
35031  * Originally Released Under LGPL - original licence link has changed is not relivant.
35032  *
35033  * Fork - LGPL
35034  * <script type="text/javascript">
35035  */
35036
35037 // private - not really -- you end up using it !
35038 // This is a support class used internally by the Grid components
35039
35040 /**
35041  * @class Roo.grid.GridEditor
35042  * @extends Roo.Editor
35043  * Class for creating and editable grid elements.
35044  * @param {Object} config any settings (must include field)
35045  */
35046 Roo.grid.GridEditor = function(field, config){
35047     if (!config && field.field) {
35048         config = field;
35049         field = Roo.factory(config.field, Roo.form);
35050     }
35051     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35052     field.monitorTab = false;
35053 };
35054
35055 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35056     
35057     /**
35058      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35059      */
35060     
35061     alignment: "tl-tl",
35062     autoSize: "width",
35063     hideEl : false,
35064     cls: "x-small-editor x-grid-editor",
35065     shim:false,
35066     shadow:"frame"
35067 });/*
35068  * Based on:
35069  * Ext JS Library 1.1.1
35070  * Copyright(c) 2006-2007, Ext JS, LLC.
35071  *
35072  * Originally Released Under LGPL - original licence link has changed is not relivant.
35073  *
35074  * Fork - LGPL
35075  * <script type="text/javascript">
35076  */
35077   
35078
35079   
35080 Roo.grid.PropertyRecord = Roo.data.Record.create([
35081     {name:'name',type:'string'},  'value'
35082 ]);
35083
35084
35085 Roo.grid.PropertyStore = function(grid, source){
35086     this.grid = grid;
35087     this.store = new Roo.data.Store({
35088         recordType : Roo.grid.PropertyRecord
35089     });
35090     this.store.on('update', this.onUpdate,  this);
35091     if(source){
35092         this.setSource(source);
35093     }
35094     Roo.grid.PropertyStore.superclass.constructor.call(this);
35095 };
35096
35097
35098
35099 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35100     setSource : function(o){
35101         this.source = o;
35102         this.store.removeAll();
35103         var data = [];
35104         for(var k in o){
35105             if(this.isEditableValue(o[k])){
35106                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35107             }
35108         }
35109         this.store.loadRecords({records: data}, {}, true);
35110     },
35111
35112     onUpdate : function(ds, record, type){
35113         if(type == Roo.data.Record.EDIT){
35114             var v = record.data['value'];
35115             var oldValue = record.modified['value'];
35116             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35117                 this.source[record.id] = v;
35118                 record.commit();
35119                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35120             }else{
35121                 record.reject();
35122             }
35123         }
35124     },
35125
35126     getProperty : function(row){
35127        return this.store.getAt(row);
35128     },
35129
35130     isEditableValue: function(val){
35131         if(val && val instanceof Date){
35132             return true;
35133         }else if(typeof val == 'object' || typeof val == 'function'){
35134             return false;
35135         }
35136         return true;
35137     },
35138
35139     setValue : function(prop, value){
35140         this.source[prop] = value;
35141         this.store.getById(prop).set('value', value);
35142     },
35143
35144     getSource : function(){
35145         return this.source;
35146     }
35147 });
35148
35149 Roo.grid.PropertyColumnModel = function(grid, store){
35150     this.grid = grid;
35151     var g = Roo.grid;
35152     g.PropertyColumnModel.superclass.constructor.call(this, [
35153         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
35154         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
35155     ]);
35156     this.store = store;
35157     this.bselect = Roo.DomHelper.append(document.body, {
35158         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
35159             {tag: 'option', value: 'true', html: 'true'},
35160             {tag: 'option', value: 'false', html: 'false'}
35161         ]
35162     });
35163     Roo.id(this.bselect);
35164     var f = Roo.form;
35165     this.editors = {
35166         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
35167         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
35168         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
35169         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
35170         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
35171     };
35172     this.renderCellDelegate = this.renderCell.createDelegate(this);
35173     this.renderPropDelegate = this.renderProp.createDelegate(this);
35174 };
35175
35176 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
35177     
35178     
35179     nameText : 'Name',
35180     valueText : 'Value',
35181     
35182     dateFormat : 'm/j/Y',
35183     
35184     
35185     renderDate : function(dateVal){
35186         return dateVal.dateFormat(this.dateFormat);
35187     },
35188
35189     renderBool : function(bVal){
35190         return bVal ? 'true' : 'false';
35191     },
35192
35193     isCellEditable : function(colIndex, rowIndex){
35194         return colIndex == 1;
35195     },
35196
35197     getRenderer : function(col){
35198         return col == 1 ?
35199             this.renderCellDelegate : this.renderPropDelegate;
35200     },
35201
35202     renderProp : function(v){
35203         return this.getPropertyName(v);
35204     },
35205
35206     renderCell : function(val){
35207         var rv = val;
35208         if(val instanceof Date){
35209             rv = this.renderDate(val);
35210         }else if(typeof val == 'boolean'){
35211             rv = this.renderBool(val);
35212         }
35213         return Roo.util.Format.htmlEncode(rv);
35214     },
35215
35216     getPropertyName : function(name){
35217         var pn = this.grid.propertyNames;
35218         return pn && pn[name] ? pn[name] : name;
35219     },
35220
35221     getCellEditor : function(colIndex, rowIndex){
35222         var p = this.store.getProperty(rowIndex);
35223         var n = p.data['name'], val = p.data['value'];
35224         
35225         if(typeof(this.grid.customEditors[n]) == 'string'){
35226             return this.editors[this.grid.customEditors[n]];
35227         }
35228         if(typeof(this.grid.customEditors[n]) != 'undefined'){
35229             return this.grid.customEditors[n];
35230         }
35231         if(val instanceof Date){
35232             return this.editors['date'];
35233         }else if(typeof val == 'number'){
35234             return this.editors['number'];
35235         }else if(typeof val == 'boolean'){
35236             return this.editors['boolean'];
35237         }else{
35238             return this.editors['string'];
35239         }
35240     }
35241 });
35242
35243 /**
35244  * @class Roo.grid.PropertyGrid
35245  * @extends Roo.grid.EditorGrid
35246  * This class represents the  interface of a component based property grid control.
35247  * <br><br>Usage:<pre><code>
35248  var grid = new Roo.grid.PropertyGrid("my-container-id", {
35249       
35250  });
35251  // set any options
35252  grid.render();
35253  * </code></pre>
35254   
35255  * @constructor
35256  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35257  * The container MUST have some type of size defined for the grid to fill. The container will be
35258  * automatically set to position relative if it isn't already.
35259  * @param {Object} config A config object that sets properties on this grid.
35260  */
35261 Roo.grid.PropertyGrid = function(container, config){
35262     config = config || {};
35263     var store = new Roo.grid.PropertyStore(this);
35264     this.store = store;
35265     var cm = new Roo.grid.PropertyColumnModel(this, store);
35266     store.store.sort('name', 'ASC');
35267     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
35268         ds: store.store,
35269         cm: cm,
35270         enableColLock:false,
35271         enableColumnMove:false,
35272         stripeRows:false,
35273         trackMouseOver: false,
35274         clicksToEdit:1
35275     }, config));
35276     this.getGridEl().addClass('x-props-grid');
35277     this.lastEditRow = null;
35278     this.on('columnresize', this.onColumnResize, this);
35279     this.addEvents({
35280          /**
35281              * @event beforepropertychange
35282              * Fires before a property changes (return false to stop?)
35283              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
35284              * @param {String} id Record Id
35285              * @param {String} newval New Value
35286          * @param {String} oldval Old Value
35287              */
35288         "beforepropertychange": true,
35289         /**
35290              * @event propertychange
35291              * Fires after a property changes
35292              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
35293              * @param {String} id Record Id
35294              * @param {String} newval New Value
35295          * @param {String} oldval Old Value
35296              */
35297         "propertychange": true
35298     });
35299     this.customEditors = this.customEditors || {};
35300 };
35301 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
35302     
35303      /**
35304      * @cfg {Object} customEditors map of colnames=> custom editors.
35305      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
35306      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
35307      * false disables editing of the field.
35308          */
35309     
35310       /**
35311      * @cfg {Object} propertyNames map of property Names to their displayed value
35312          */
35313     
35314     render : function(){
35315         Roo.grid.PropertyGrid.superclass.render.call(this);
35316         this.autoSize.defer(100, this);
35317     },
35318
35319     autoSize : function(){
35320         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
35321         if(this.view){
35322             this.view.fitColumns();
35323         }
35324     },
35325
35326     onColumnResize : function(){
35327         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
35328         this.autoSize();
35329     },
35330     /**
35331      * Sets the data for the Grid
35332      * accepts a Key => Value object of all the elements avaiable.
35333      * @param {Object} data  to appear in grid.
35334      */
35335     setSource : function(source){
35336         this.store.setSource(source);
35337         //this.autoSize();
35338     },
35339     /**
35340      * Gets all the data from the grid.
35341      * @return {Object} data  data stored in grid
35342      */
35343     getSource : function(){
35344         return this.store.getSource();
35345     }
35346 });/*
35347  * Based on:
35348  * Ext JS Library 1.1.1
35349  * Copyright(c) 2006-2007, Ext JS, LLC.
35350  *
35351  * Originally Released Under LGPL - original licence link has changed is not relivant.
35352  *
35353  * Fork - LGPL
35354  * <script type="text/javascript">
35355  */
35356  
35357 /**
35358  * @class Roo.LoadMask
35359  * A simple utility class for generically masking elements while loading data.  If the element being masked has
35360  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
35361  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
35362  * element's UpdateManager load indicator and will be destroyed after the initial load.
35363  * @constructor
35364  * Create a new LoadMask
35365  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
35366  * @param {Object} config The config object
35367  */
35368 Roo.LoadMask = function(el, config){
35369     this.el = Roo.get(el);
35370     Roo.apply(this, config);
35371     if(this.store){
35372         this.store.on('beforeload', this.onBeforeLoad, this);
35373         this.store.on('load', this.onLoad, this);
35374         this.store.on('loadexception', this.onLoad, this);
35375         this.removeMask = false;
35376     }else{
35377         var um = this.el.getUpdateManager();
35378         um.showLoadIndicator = false; // disable the default indicator
35379         um.on('beforeupdate', this.onBeforeLoad, this);
35380         um.on('update', this.onLoad, this);
35381         um.on('failure', this.onLoad, this);
35382         this.removeMask = true;
35383     }
35384 };
35385
35386 Roo.LoadMask.prototype = {
35387     /**
35388      * @cfg {Boolean} removeMask
35389      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
35390      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
35391      */
35392     /**
35393      * @cfg {String} msg
35394      * The text to display in a centered loading message box (defaults to 'Loading...')
35395      */
35396     msg : 'Loading...',
35397     /**
35398      * @cfg {String} msgCls
35399      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
35400      */
35401     msgCls : 'x-mask-loading',
35402
35403     /**
35404      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
35405      * @type Boolean
35406      */
35407     disabled: false,
35408
35409     /**
35410      * Disables the mask to prevent it from being displayed
35411      */
35412     disable : function(){
35413        this.disabled = true;
35414     },
35415
35416     /**
35417      * Enables the mask so that it can be displayed
35418      */
35419     enable : function(){
35420         this.disabled = false;
35421     },
35422
35423     // private
35424     onLoad : function(){
35425         this.el.unmask(this.removeMask);
35426     },
35427
35428     // private
35429     onBeforeLoad : function(){
35430         if(!this.disabled){
35431             this.el.mask(this.msg, this.msgCls);
35432         }
35433     },
35434
35435     // private
35436     destroy : function(){
35437         if(this.store){
35438             this.store.un('beforeload', this.onBeforeLoad, this);
35439             this.store.un('load', this.onLoad, this);
35440             this.store.un('loadexception', this.onLoad, this);
35441         }else{
35442             var um = this.el.getUpdateManager();
35443             um.un('beforeupdate', this.onBeforeLoad, this);
35444             um.un('update', this.onLoad, this);
35445             um.un('failure', this.onLoad, this);
35446         }
35447     }
35448 };/*
35449  * Based on:
35450  * Ext JS Library 1.1.1
35451  * Copyright(c) 2006-2007, Ext JS, LLC.
35452  *
35453  * Originally Released Under LGPL - original licence link has changed is not relivant.
35454  *
35455  * Fork - LGPL
35456  * <script type="text/javascript">
35457  */
35458 Roo.XTemplate = function(){
35459     Roo.XTemplate.superclass.constructor.apply(this, arguments);
35460     var s = this.html;
35461
35462     s = ['<tpl>', s, '</tpl>'].join('');
35463
35464     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
35465
35466     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
35467     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
35468     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
35469     var m, id = 0;
35470     var tpls = [];
35471
35472     while(m = s.match(re)){
35473        var m2 = m[0].match(nameRe);
35474        var m3 = m[0].match(ifRe);
35475        var m4 = m[0].match(execRe);
35476        var exp = null, fn = null, exec = null;
35477        var name = m2 && m2[1] ? m2[1] : '';
35478        if(m3){
35479            exp = m3 && m3[1] ? m3[1] : null;
35480            if(exp){
35481                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
35482            }
35483        }
35484        if(m4){
35485            exp = m4 && m4[1] ? m4[1] : null;
35486            if(exp){
35487                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
35488            }
35489        }
35490        if(name){
35491            switch(name){
35492                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
35493                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
35494                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
35495            }
35496        }
35497        tpls.push({
35498             id: id,
35499             target: name,
35500             exec: exec,
35501             test: fn,
35502             body: m[1]||''
35503         });
35504        s = s.replace(m[0], '{xtpl'+ id + '}');
35505        ++id;
35506     }
35507     for(var i = tpls.length-1; i >= 0; --i){
35508         this.compileTpl(tpls[i]);
35509     }
35510     this.master = tpls[tpls.length-1];
35511     this.tpls = tpls;
35512 };
35513 Roo.extend(Roo.XTemplate, Roo.Template, {
35514
35515     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
35516
35517     applySubTemplate : function(id, values, parent){
35518         var t = this.tpls[id];
35519         if(t.test && !t.test.call(this, values, parent)){
35520             return '';
35521         }
35522         if(t.exec && t.exec.call(this, values, parent)){
35523             return '';
35524         }
35525         var vs = t.target ? t.target.call(this, values, parent) : values;
35526         parent = t.target ? values : parent;
35527         if(t.target && vs instanceof Array){
35528             var buf = [];
35529             for(var i = 0, len = vs.length; i < len; i++){
35530                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
35531             }
35532             return buf.join('');
35533         }
35534         return t.compiled.call(this, vs, parent);
35535     },
35536
35537     compileTpl : function(tpl){
35538         var fm = Roo.util.Format;
35539         var useF = this.disableFormats !== true;
35540         var sep = Roo.isGecko ? "+" : ",";
35541         var fn = function(m, name, format, args){
35542             if(name.substr(0, 4) == 'xtpl'){
35543                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
35544             }
35545             var v;
35546             if(name.indexOf('.') != -1){
35547                 v = name;
35548             }else{
35549                 v = "values['" + name + "']";
35550             }
35551             if(format && useF){
35552                 args = args ? ',' + args : "";
35553                 if(format.substr(0, 5) != "this."){
35554                     format = "fm." + format + '(';
35555                 }else{
35556                     format = 'this.call("'+ format.substr(5) + '", ';
35557                     args = ", values";
35558                 }
35559             }else{
35560                 args= ''; format = "("+v+" === undefined ? '' : ";
35561             }
35562             return "'"+ sep + format + v + args + ")"+sep+"'";
35563         };
35564         var body;
35565         // branched to use + in gecko and [].join() in others
35566         if(Roo.isGecko){
35567             body = "tpl.compiled = function(values, parent){ return '" +
35568                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
35569                     "';};";
35570         }else{
35571             body = ["tpl.compiled = function(values, parent){ return ['"];
35572             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
35573             body.push("'].join('');};");
35574             body = body.join('');
35575         }
35576         /** eval:var:zzzzzzz */
35577         eval(body);
35578         return this;
35579     },
35580
35581     applyTemplate : function(values){
35582         return this.master.compiled.call(this, values, {});
35583         var s = this.subs;
35584     },
35585
35586     apply : function(){
35587         return this.applyTemplate.apply(this, arguments);
35588     },
35589
35590     compile : function(){return this;}
35591 });
35592
35593 Roo.XTemplate.from = function(el){
35594     el = Roo.getDom(el);
35595     return new Roo.XTemplate(el.value || el.innerHTML);
35596 };/*
35597  * Original code for Roojs - LGPL
35598  * <script type="text/javascript">
35599  */
35600  
35601 /**
35602  * @class Roo.XComponent
35603  * A delayed Element creator...
35604  * 
35605  * Mypart.xyx = new Roo.XComponent({
35606
35607     parent : 'Mypart.xyz', // empty == document.element.!!
35608     order : '001',
35609     name : 'xxxx'
35610     region : 'xxxx'
35611     disabled : function() {} 
35612      
35613     tree : function() { // return an tree of xtype declared components
35614         var MODULE = this;
35615         return 
35616         {
35617             xtype : 'NestedLayoutPanel',
35618             // technicall
35619         }
35620      ]
35621  *})
35622  * @extends Roo.util.Observable
35623  * @constructor
35624  * @param cfg {Object} configuration of component
35625  * 
35626  */
35627 Roo.XComponent = function(cfg) {
35628     Roo.apply(this, cfg);
35629     this.addEvents({ 
35630         /**
35631              * @event built
35632              * Fires when this the componnt is built
35633              * @param {Roo.XComponent} c the component
35634              */
35635         'built' : true,
35636         /**
35637              * @event buildcomplete
35638              * Fires on the top level element when all elements have been built
35639              * @param {Roo.XComponent} c the top level component.
35640          */
35641         'buildcomplete' : true,
35642         
35643     });
35644     
35645     Roo.XComponent.register(this);
35646     this.modules = false;
35647     this.el = false; // where the layout goes..
35648     
35649     
35650 }
35651 Roo.extend(Roo.XComponent, Roo.util.Observable, {
35652     /**
35653      * @property el
35654      * The created element (with Roo.factory())
35655      * @type {Roo.Layout}
35656      */
35657     el  : false,
35658     
35659     /**
35660      * @property el
35661      * for BC  - use el in new code
35662      * @type {Roo.Layout}
35663      */
35664     panel : false,
35665     
35666     /**
35667      * @property layout
35668      * for BC  - use el in new code
35669      * @type {Roo.Layout}
35670      */
35671     layout : false,
35672     
35673      /**
35674      * @cfg {Function|boolean} disabled
35675      * If this module is disabled by some rule, return true from the funtion
35676      */
35677     disabled : false,
35678     
35679     /**
35680      * @cfg {String} parent 
35681      * Name of parent element which it get xtype added to..
35682      */
35683     parent: false,
35684     
35685     /**
35686      * @cfg {String} order
35687      * Used to set the order in which elements are created (usefull for multiple tabs)
35688      */
35689     
35690     order : false,
35691     /**
35692      * @cfg {String} name
35693      * String to display while loading.
35694      */
35695     name : false,
35696     /**
35697      * @cfg {Array} items
35698      * A single item array - the first element is the root of the tree..
35699      * It's done this way to stay compatible with the Xtype system...
35700      */
35701     items : false,
35702      
35703      
35704     
35705 });
35706
35707 Roo.apply(Roo.XComponent, {
35708     
35709     /**
35710      * @property  buildCompleted
35711      * True when the builder has completed building the interface.
35712      * @type Boolean
35713      */
35714     buildCompleted : false,
35715      
35716     /**
35717      * @property  topModule
35718      * the upper most module - uses document.element as it's constructor.
35719      * @type Object
35720      */
35721      
35722     topModule  : false,
35723       
35724     /**
35725      * @property  modules
35726      * array of modules to be created by registration system.
35727      * @type Roo.XComponent
35728      */
35729     
35730     modules : [],
35731       
35732     
35733     /**
35734      * Register components to be built later.
35735      *
35736      * This solves the following issues
35737      * - Building is not done on page load, but after an authentication process has occured.
35738      * - Interface elements are registered on page load
35739      * - Parent Interface elements may not be loaded before child, so this handles that..
35740      * 
35741      *
35742      * example:
35743      * 
35744      * MyApp.register({
35745           order : '000001',
35746           module : 'Pman.Tab.projectMgr',
35747           region : 'center',
35748           parent : 'Pman.layout',
35749           disabled : false,  // or use a function..
35750         })
35751      
35752      * * @param {Object} details about module
35753      */
35754     register : function(obj) {
35755         this.modules.push(obj);
35756          
35757     },
35758     /**
35759      * convert a string to an object..
35760      * 
35761      */
35762     
35763     toObject : function(str)
35764     {
35765         if (!str || typeof(str) == 'object') {
35766             return str;
35767         }
35768         var ar = str.split('.');
35769         var rt, o;
35770         rt = ar.shift();
35771             /** eval:var:o */
35772         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
35773         if (o === false) {
35774             throw "Module not found : " + str;
35775         }
35776         Roo.each(ar, function(e) {
35777             if (typeof(o[e]) == 'undefined') {
35778                 throw "Module not found : " + str;
35779             }
35780             o = o[e];
35781         });
35782         return o;
35783         
35784     },
35785     
35786     
35787     /**
35788      * move modules into their correct place in the tree..
35789      * 
35790      */
35791     preBuild : function ()
35792     {
35793         
35794         Roo.each(this.modules , function (obj)
35795         {
35796             obj.parent = this.toObject(obj.parent);
35797             
35798             if (!obj.parent) {
35799                 this.topModule = obj;
35800                 return;
35801             }
35802             
35803             if (!obj.parent.modules) {
35804                 obj.parent.modules = new Roo.util.MixedCollection(false, 
35805                     function(o) { return o.order + '' }
35806                 );
35807             }
35808             
35809             obj.parent.modules.add(obj);
35810         }, this);
35811     },
35812     
35813      /**
35814      * make a list of modules to build.
35815      * @return {Array} list of modules. 
35816      */ 
35817     
35818     buildOrder : function()
35819     {
35820         var _this = this;
35821         var cmp = function(a,b) {   
35822             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
35823         };
35824         
35825         if (!this.topModule || !this.topModule.modules) {
35826             throw "No top level modules to build";
35827         }
35828        
35829         // make a flat list in order of modules to build.
35830         var mods = [ this.topModule ];
35831         
35832         
35833         // add modules to their parents..
35834         var addMod = function(m) {
35835            // Roo.debug && Roo.log(m.modKey);
35836             
35837             mods.push(m);
35838             if (m.modules) {
35839                 m.modules.keySort('ASC',  cmp );
35840                 m.modules.each(addMod);
35841             }
35842             // not sure if this is used any more..
35843             if (m.finalize) {
35844                 m.finalize.name = m.name + " (clean up) ";
35845                 mods.push(m.finalize);
35846             }
35847             
35848         }
35849         this.topModule.modules.keySort('ASC',  cmp );
35850         this.topModule.modules.each(addMod);
35851         return mods;
35852     },
35853     
35854      /**
35855      * Build the registered modules.
35856      * @param {Object} parent element.
35857      * @param {Function} optional method to call after module has been added.
35858      * 
35859      */ 
35860    
35861     build : function() 
35862     {
35863         
35864         this.preBuild();
35865         var mods = this.buildOrder();
35866       
35867         //this.allmods = mods;
35868         //Roo.debug && Roo.log(mods);
35869         //return;
35870         if (!mods.length) { // should not happen
35871             throw "NO modules!!!";
35872         }
35873         
35874         
35875         
35876         // flash it up as modal - so we store the mask!?
35877         Roo.MessageBox.show({ title: 'loading' });
35878         Roo.MessageBox.show({
35879            title: "Please wait...",
35880            msg: "Building Interface...",
35881            width:450,
35882            progress:true,
35883            closable:false,
35884            modal: false
35885           
35886         });
35887         var total = mods.length;
35888         
35889         var _this = this;
35890         var progressRun = function() {
35891             if (!mods.length) {
35892                 Roo.debug && Roo.log('hide?');
35893                 Roo.MessageBox.hide();
35894                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
35895                 return;    
35896             }
35897             
35898             var m = mods.shift();
35899             Roo.debug && Roo.log(m);
35900             if (typeof(m) == 'function') { // not sure if this is supported any more..
35901                 m.call(this);
35902                 return progressRun.defer(10, _this);
35903             } 
35904             
35905             Roo.MessageBox.updateProgress(
35906                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
35907                     " of " + total + 
35908                     (m.name ? (' - ' + m.name) : '')
35909                     );
35910             
35911          
35912             
35913             var disabled = (typeof(m.disabled) == 'function') ?
35914                 m.disabled.call(m.module.disabled) : m.disabled;    
35915             
35916             
35917             if (disabled) {
35918                 return progressRun(); // we do not update the display!
35919             }
35920             
35921             if (!m.parent) {
35922                 // it's a top level one..
35923                 var layoutbase = new Ext.BorderLayout(document.body, {
35924                
35925                     center: {
35926                          titlebar: false,
35927                          autoScroll:false,
35928                          closeOnTab: true,
35929                          tabPosition: 'top',
35930                          //resizeTabs: true,
35931                          alwaysShowTabs: true,
35932                          minTabWidth: 140
35933                     }
35934                 });
35935                 var tree = m.tree();
35936                 tree.region = 'center';
35937                 m.el = layoutbase.addxtype(tree);
35938                 m.panel = m.el;
35939                 m.layout = m.panel.layout;    
35940                 return progressRun.defer(10, _this);
35941             }
35942             
35943             var tree = m.tree();
35944             tree.region = tree.region || m.region;
35945             m.el = m.parent.el.addxtype(tree);
35946             m.fireEvent('built', m);
35947             m.panel = m.el;
35948             m.layout = m.panel.layout;    
35949             progressRun.defer(10, _this); 
35950             
35951         }
35952         progressRun.defer(1, _this);
35953      
35954         
35955         
35956     }
35957      
35958    
35959     
35960     
35961 });
35962  //<script type="text/javascript">
35963
35964
35965 /**
35966  * @class Roo.Login
35967  * @extends Roo.LayoutDialog
35968  * A generic Login Dialog..... - only one needed in theory!?!?
35969  *
35970  * Fires XComponent builder on success...
35971  * 
35972  * Sends 
35973  *    username,password, lang = for login actions.
35974  *    check = 1 for periodic checking that sesion is valid.
35975  *    passwordRequest = email request password
35976  *    logout = 1 = to logout
35977  * 
35978  * Affects: (this id="????" elements)
35979  *   loading  (removed) (used to indicate application is loading)
35980  *   loading-mask (hides) (used to hide application when it's building loading)
35981  *   
35982  * 
35983  * Usage: 
35984  *    
35985  * 
35986  * Myapp.login = Roo.Login({
35987      url: xxxx,
35988    
35989      realm : 'Myapp', 
35990      
35991      
35992      method : 'POST',
35993      
35994      
35995      * 
35996  })
35997  * 
35998  * 
35999  * 
36000  **/
36001  
36002 Roo.Login = function(cfg)
36003 {
36004     this.addEvents({
36005         'refreshed' : true,
36006     });
36007     
36008     Roo.apply(this,cfg);
36009     
36010     Roo.onReady(function() {
36011         this.onLoad();
36012     }, this);
36013     // call parent..
36014     
36015    
36016     Roo.Login.superclass.constructor.call(this, this);
36017     //this.addxtype(this.items[0]);
36018     
36019     
36020 }
36021
36022
36023 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36024     
36025     /**
36026      * @cfg {String} method
36027      * Method used to query for login details.
36028      */
36029     
36030     method : 'POST',
36031     /**
36032      * @cfg {String} url
36033      * URL to query login data. - eg. baseURL + '/Login.php'
36034      */
36035     url : '',
36036     
36037     /**
36038      * @property user
36039      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36040      * @type {Object} 
36041      */
36042     user : false,
36043     /**
36044      * @property checkFails
36045      * Number of times we have attempted to get authentication check, and failed.
36046      * @type {Number} 
36047      */
36048     checkFails : 0,
36049       /**
36050      * @property intervalID
36051      * The window interval that does the constant login checking.
36052      * @type {Number} 
36053      */
36054     intervalID : 0,
36055     
36056     
36057     onLoad : function() // called on page load...
36058     {
36059         // load 
36060          
36061         if (Roo.get('loading')) { // clear any loading indicator..
36062             Roo.get('loading').remove();
36063         }
36064         
36065         //this.switchLang('en'); // set the language to english..
36066        
36067         this.check({
36068             success:  function(response, opts)  {  // check successfull...
36069             
36070                 var res = this.processResponse(response);
36071                 this.checkFails =0;
36072                 if (!res.success) { // error!
36073                     this.checkFails = 5;
36074                     //console.log('call failure');
36075                     return this.failure(response,opts);
36076                 }
36077                 
36078                 if (!res.data.id) { // id=0 == login failure.
36079                     return this.show();
36080                 }
36081                 
36082                               
36083                         //console.log(success);
36084                 this.fillAuth(res.data);   
36085                 this.checkFails =0;
36086                 Roo.XComponent.build();
36087             },
36088             failure : this.show
36089         });
36090         
36091     }, 
36092     
36093     
36094     check: function(cfg) // called every so often to refresh cookie etc..
36095     {
36096         if (cfg.again) { // could be undefined..
36097             this.checkFails++;
36098         } else {
36099             this.checkFails = 0;
36100         }
36101         var _this = this;
36102         if (this.sending) {
36103             if ( this.checkFails > 4) {
36104                 Roo.MessageBox.alert("Error",  
36105                     "Error getting authentication status. - try reloading, or wait a while", function() {
36106                         _this.sending = false;
36107                     }); 
36108                 return;
36109             }
36110             cfg.again = true;
36111             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36112             return;
36113         }
36114         this.sending = true;
36115         
36116         Roo.Ajax.request({  
36117             url: this.url,
36118             params: {
36119                 getAuthUser: true
36120             },  
36121             method: this.method,
36122             success:  cfg.success || this.success,
36123             failure : cfg.failure || this.failure,
36124             scope : this,
36125             callCfg : cfg
36126               
36127         });  
36128     }, 
36129     
36130     
36131     logout: function()
36132     {
36133         window.onbeforeunload = function() { }; // false does not work for IE..
36134         this.user = false;
36135         var _this = this;
36136         
36137         Roo.Ajax.request({  
36138             url: this.url,
36139             params: {
36140                 logout: 1
36141             },  
36142             method: 'GET',
36143             failure : function() {
36144                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36145                     document.location = document.location.toString() + '?ts=' + Math.random();
36146                 });
36147                 
36148             },
36149             success : function() {
36150                 _this.user = false;
36151                 this.checkFails =0;
36152                 // fixme..
36153                 document.location = document.location.toString() + '?ts=' + Math.random();
36154             }
36155               
36156               
36157         }); 
36158     },
36159     
36160     processResponse : function (response)
36161     {
36162         var res = '';
36163         try {
36164             res = Roo.decode(response.responseText);
36165             // oops...
36166             if (typeof(res) != 'object') {
36167                 res = { success : false, errorMsg : res, errors : true };
36168             }
36169             if (typeof(res.success) == 'undefined') {
36170                 res.success = false;
36171             }
36172             
36173         } catch(e) {
36174             res = { success : false,  errorMsg : response.responseText, errors : true };
36175         }
36176         return res;
36177     },
36178     
36179     success : function(response, opts)  // check successfull...
36180     {  
36181         this.sending = false;
36182         var res = this.processResponse(response);
36183         if (!res.success) {
36184             return this.failure(response, opts);
36185         }
36186         if (!res.data || !res.data.id) {
36187             return this.failure(response,opts);
36188         }
36189         //console.log(res);
36190         this.fillAuth(res.data);
36191         
36192         this.checkFails =0;
36193         
36194     },
36195     
36196     
36197     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
36198     {
36199         this.authUser = -1;
36200         this.sending = false;
36201         var res = this.processResponse(response);
36202         //console.log(res);
36203         if ( this.checkFails > 2) {
36204         
36205             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
36206                 "Error getting authentication status. - try reloading"); 
36207             return;
36208         }
36209         opts.callCfg.again = true;
36210         this.check.defer(1000, this, [ opts.callCfg ]);
36211         return;  
36212     },
36213     
36214     
36215     
36216     fillAuth: function(au) {
36217         this.startAuthCheck();
36218         this.authUserId = au.id;
36219         this.authUser = au;
36220         this.lastChecked = new Date();
36221         this.fireEvent('refreshed', au);
36222         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
36223         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
36224         au.lang = au.lang || 'en';
36225         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
36226         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
36227         this.switchLang(au.lang );
36228         
36229      
36230         // open system... - -on setyp..
36231         if (this.authUserId  < 0) {
36232             Roo.MessageBox.alert("Warning", 
36233                 "This is an open system - please set up a admin user with a password.");  
36234         }
36235          
36236         //Pman.onload(); // which should do nothing if it's a re-auth result...
36237         
36238              
36239     },
36240     
36241     startAuthCheck : function() // starter for timeout checking..
36242     {
36243         if (this.intervalID) { // timer already in place...
36244             return false;
36245         }
36246         var _this = this;
36247         this.intervalID =  window.setInterval(function() {
36248               _this.check(false);
36249             }, 120000); // every 120 secs = 2mins..
36250         
36251         
36252     },
36253          
36254     
36255     switchLang : function (lang) 
36256     {
36257         _T = typeof(_T) == 'undefined' ? false : _T;
36258           if (!_T || !lang.length) {
36259             return;
36260         }
36261         
36262         if (!_T && lang != 'en') {
36263             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36264             return;
36265         }
36266         
36267         if (typeof(_T.en) == 'undefined') {
36268             _T.en = {};
36269             Roo.apply(_T.en, _T);
36270         }
36271         
36272         if (typeof(_T[lang]) == 'undefined') {
36273             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36274             return;
36275         }
36276         
36277         
36278         Roo.apply(_T, _T[lang]);
36279         // just need to set the text values for everything...
36280         var _this = this;
36281         /* this will not work ...
36282         if (this.form) { 
36283             
36284                
36285             function formLabel(name, val) {
36286                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
36287             }
36288             
36289             formLabel('password', "Password"+':');
36290             formLabel('username', "Email Address"+':');
36291             formLabel('lang', "Language"+':');
36292             this.dialog.setTitle("Login");
36293             this.dialog.buttons[0].setText("Forgot Password");
36294             this.dialog.buttons[1].setText("Login");
36295         }
36296         */
36297         
36298         
36299     },
36300     
36301     
36302     title: "Login",
36303     modal: true,
36304     width:  350,
36305     //height: 230,
36306     height: 180,
36307     shadow: true,
36308     minWidth:200,
36309     minHeight:180,
36310     //proxyDrag: true,
36311     closable: false,
36312     draggable: false,
36313     collapsible: false,
36314     resizable: false,
36315     center: {  // needed??
36316         autoScroll:false,
36317         titlebar: false,
36318        // tabPosition: 'top',
36319         hideTabs: true,
36320         closeOnTab: true,
36321         alwaysShowTabs: false
36322     } ,
36323     listeners : {
36324         
36325         show  : function(dlg)
36326         {
36327             //console.log(this);
36328             this.form = this.layout.getRegion('center').activePanel.form;
36329             this.form.dialog = dlg;
36330             this.buttons[0].form = this.form;
36331             this.buttons[0].dialog = dlg
36332             this.buttons[1].form = this.form;
36333             this.buttons[1].dialog = dlg;
36334            
36335            //this.resizeToLogo.defer(1000,this);
36336             // this is all related to resizing for logos..
36337             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
36338            //// if (!sz) {
36339              //   this.resizeToLogo.defer(1000,this);
36340              //   return;
36341            // }
36342             //var w = Ext.lib.Dom.getViewWidth() - 100;
36343             //var h = Ext.lib.Dom.getViewHeight() - 100;
36344             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
36345             //this.center();
36346             if (this.disabled) {
36347                 this.hide();
36348                 return;
36349             }
36350             
36351             if (this.user.id < 0) { // used for inital setup situations.
36352                 return;
36353             }
36354             
36355             if (this.intervalID) {
36356                 // remove the timer
36357                 window.clearInterval(this.intervalID);
36358                 this.intervalID = false;
36359             }
36360             
36361             
36362             if (Roo.get('loading')) {
36363                 Roo.get('loading').remove();
36364             }
36365             if (Roo.get('loading-mask')) {
36366                 Roo.get('loading-mask').hide();
36367             }
36368             
36369             //incomming._node = tnode;
36370             this.form.reset();
36371             //this.dialog.modal = !modal;
36372             //this.dialog.show();
36373             this.el.unmask(); 
36374             
36375             
36376             this.form.setValues({
36377                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
36378                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
36379             });
36380             
36381             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
36382             if (this.form.findField('username').getValue().length > 0 ){
36383                 this.form.findField('password').focus();
36384             } else {
36385                this.form.findField('username').focus();
36386             }
36387     
36388         }
36389     },
36390     items : [
36391          {
36392        
36393             xtype : 'ContentPanel',
36394             xns : Roo,
36395             region: 'center',
36396             fitToFrame : true,
36397             
36398             items : [
36399     
36400                 {
36401                
36402                     xtype : 'Form',
36403                     xns : Roo.form,
36404                     labelWidth: 100,
36405                     style : 'margin: 10px;',
36406                     
36407                     listeners : {
36408                         actionfailed : function(f, act) {
36409                             // form can return { errors: .... }
36410                                 
36411                             //act.result.errors // invalid form element list...
36412                             //act.result.errorMsg// invalid form element list...
36413                             
36414                             this.dialog.el.unmask();
36415                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
36416                                         "Login failed - communication error - try again.");
36417                                       
36418                         },
36419                         actioncomplete: function(re, act) {
36420                              
36421                             Roo.state.Manager.set(
36422                                 this.dialog.realm + '.username',  
36423                                     this.findField('username').getValue()
36424                             );
36425                             Roo.state.Manager.set(
36426                                 this.dialog.realm + '.lang',  
36427                                 this.findField('lang').getValue() 
36428                             );
36429                             
36430                             this.dialog.fillAuth(act.result.data);
36431                               
36432                             this.dialog.hide();
36433                             
36434                             if (Roo.get('loading-mask')) {
36435                                 Roo.get('loading-mask').show();
36436                             }
36437                             Roo.XComponent.build();
36438                             
36439                              
36440                             
36441                         }
36442                     },
36443                     items : [
36444                         {
36445                             xtype : 'TextField',
36446                             xns : Roo.form,
36447                             fieldLabel: "Email Address",
36448                             name: 'username',
36449                             width:200,
36450                             autoCreate : {tag: "input", type: "text", size: "20"}
36451                         },
36452                         {
36453                             xtype : 'TextField',
36454                             xns : Roo.form,
36455                             fieldLabel: "Password",
36456                             inputType: 'password',
36457                             name: 'password',
36458                             width:200,
36459                             autoCreate : {tag: "input", type: "text", size: "20"},
36460                             listeners : {
36461                                 specialkey : function(e,ev) {
36462                                     if (ev.keyCode == 13) {
36463                                         this.form.dialog.el.mask("Logging in");
36464                                         this.form.doAction('submit', {
36465                                             url: this.form.dialog.url,
36466                                             method: this.form.dialog.method,
36467                                         });
36468                                     }
36469                                 }
36470                             }  
36471                         },
36472                         {
36473                             xtype : 'ComboBox',
36474                             xns : Roo.form,
36475                             fieldLabel: "Language",
36476                             name : 'langdisp',
36477                             store: {
36478                                 xtype : 'SimpleStore',
36479                                 fields: ['lang', 'ldisp'],
36480                                 data : [
36481                                     [ 'en', 'English' ],
36482                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
36483                                     [ 'zh_CN', '\u7C21\u4E2D' ]
36484                                 ]
36485                             },
36486                             
36487                             valueField : 'lang',
36488                             hiddenName:  'lang',
36489                             width: 200,
36490                             displayField:'ldisp',
36491                             typeAhead: false,
36492                             editable: false,
36493                             mode: 'local',
36494                             triggerAction: 'all',
36495                             emptyText:'Select a Language...',
36496                             selectOnFocus:true,
36497                             listeners : {
36498                                 select :  function(cb, rec, ix) {
36499                                     this.form.switchLang(rec.data.lang);
36500                                 }
36501                             }
36502                         
36503                         }
36504                     ]
36505                 }
36506                   
36507                 
36508             ]
36509         }
36510     ],
36511     buttons : [
36512         {
36513             xtype : 'Button',
36514             xns : 'Roo',
36515             text : "Forgot Password",
36516             listeners : {
36517                 click : function() {
36518                     //console.log(this);
36519                     var n = this.form.findField('username').getValue();
36520                     if (!n.length) {
36521                         Roo.MessageBox.alert("Error", "Fill in your email address");
36522                         return;
36523                     }
36524                     Roo.Ajax.request({
36525                         url: this.dialog.url,
36526                         params: {
36527                             passwordRequest: n
36528                         },
36529                         method: this.dialog.method,
36530                         success:  function(response, opts)  {  // check successfull...
36531                         
36532                             var res = this.dialog.processResponse(response);
36533                             if (!res.success) { // error!
36534                                Roo.MessageBox.alert("Error" ,
36535                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
36536                                return;
36537                             }
36538                             Roo.MessageBox.alert("Notice" ,
36539                                 "Please check you email for the Password Reset message");
36540                         },
36541                         failure : function() {
36542                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
36543                         }
36544                         
36545                     });
36546                 }
36547             }
36548         },
36549         {
36550             xtype : 'Button',
36551             xns : 'Roo',
36552             text : "Login",
36553             listeners : {
36554                 
36555                 click : function () {
36556                         
36557                     this.dialog.el.mask("Logging in");
36558                     this.form.doAction('submit', {
36559                             url: this.dialog.url,
36560                             method: this.dialog.method
36561                     });
36562                 }
36563             }
36564         }
36565     ]
36566   
36567   
36568 })
36569  
36570
36571
36572