roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75 };
76
77 Roo.dd.DragDrop.prototype = {
78
79     /**
80      * The id of the element associated with this object.  This is what we
81      * refer to as the "linked element" because the size and position of
82      * this element is used to determine when the drag and drop objects have
83      * interacted.
84      * @property id
85      * @type String
86      */
87     id: null,
88
89     /**
90      * Configuration attributes passed into the constructor
91      * @property config
92      * @type object
93      */
94     config: null,
95
96     /**
97      * The id of the element that will be dragged.  By default this is same
98      * as the linked element , but could be changed to another element. Ex:
99      * Roo.dd.DDProxy
100      * @property dragElId
101      * @type String
102      * @private
103      */
104     dragElId: null,
105
106     /**
107      * the id of the element that initiates the drag operation.  By default
108      * this is the linked element, but could be changed to be a child of this
109      * element.  This lets us do things like only starting the drag when the
110      * header element within the linked html element is clicked.
111      * @property handleElId
112      * @type String
113      * @private
114      */
115     handleElId: null,
116
117     /**
118      * An associative array of HTML tags that will be ignored if clicked.
119      * @property invalidHandleTypes
120      * @type {string: string}
121      */
122     invalidHandleTypes: null,
123
124     /**
125      * An associative array of ids for elements that will be ignored if clicked
126      * @property invalidHandleIds
127      * @type {string: string}
128      */
129     invalidHandleIds: null,
130
131     /**
132      * An indexted array of css class names for elements that will be ignored
133      * if clicked.
134      * @property invalidHandleClasses
135      * @type string[]
136      */
137     invalidHandleClasses: null,
138
139     /**
140      * The linked element's absolute X position at the time the drag was
141      * started
142      * @property startPageX
143      * @type int
144      * @private
145      */
146     startPageX: 0,
147
148     /**
149      * The linked element's absolute X position at the time the drag was
150      * started
151      * @property startPageY
152      * @type int
153      * @private
154      */
155     startPageY: 0,
156
157     /**
158      * The group defines a logical collection of DragDrop objects that are
159      * related.  Instances only get events when interacting with other
160      * DragDrop object in the same group.  This lets us define multiple
161      * groups using a single DragDrop subclass if we want.
162      * @property groups
163      * @type {string: string}
164      */
165     groups: null,
166
167     /**
168      * Individual drag/drop instances can be locked.  This will prevent
169      * onmousedown start drag.
170      * @property locked
171      * @type boolean
172      * @private
173      */
174     locked: false,
175
176     /**
177      * Lock this instance
178      * @method lock
179      */
180     lock: function() { this.locked = true; },
181
182     /**
183      * Unlock this instace
184      * @method unlock
185      */
186     unlock: function() { this.locked = false; },
187
188     /**
189      * By default, all insances can be a drop target.  This can be disabled by
190      * setting isTarget to false.
191      * @method isTarget
192      * @type boolean
193      */
194     isTarget: true,
195
196     /**
197      * The padding configured for this drag and drop object for calculating
198      * the drop zone intersection with this object.
199      * @method padding
200      * @type int[]
201      */
202     padding: null,
203
204     /**
205      * Cached reference to the linked element
206      * @property _domRef
207      * @private
208      */
209     _domRef: null,
210
211     /**
212      * Internal typeof flag
213      * @property __ygDragDrop
214      * @private
215      */
216     __ygDragDrop: true,
217
218     /**
219      * Set to true when horizontal contraints are applied
220      * @property constrainX
221      * @type boolean
222      * @private
223      */
224     constrainX: false,
225
226     /**
227      * Set to true when vertical contraints are applied
228      * @property constrainY
229      * @type boolean
230      * @private
231      */
232     constrainY: false,
233
234     /**
235      * The left constraint
236      * @property minX
237      * @type int
238      * @private
239      */
240     minX: 0,
241
242     /**
243      * The right constraint
244      * @property maxX
245      * @type int
246      * @private
247      */
248     maxX: 0,
249
250     /**
251      * The up constraint
252      * @property minY
253      * @type int
254      * @type int
255      * @private
256      */
257     minY: 0,
258
259     /**
260      * The down constraint
261      * @property maxY
262      * @type int
263      * @private
264      */
265     maxY: 0,
266
267     /**
268      * Maintain offsets when we resetconstraints.  Set to true when you want
269      * the position of the element relative to its parent to stay the same
270      * when the page changes
271      *
272      * @property maintainOffset
273      * @type boolean
274      */
275     maintainOffset: false,
276
277     /**
278      * Array of pixel locations the element will snap to if we specified a
279      * horizontal graduation/interval.  This array is generated automatically
280      * when you define a tick interval.
281      * @property xTicks
282      * @type int[]
283      */
284     xTicks: null,
285
286     /**
287      * Array of pixel locations the element will snap to if we specified a
288      * vertical graduation/interval.  This array is generated automatically
289      * when you define a tick interval.
290      * @property yTicks
291      * @type int[]
292      */
293     yTicks: null,
294
295     /**
296      * By default the drag and drop instance will only respond to the primary
297      * button click (left button for a right-handed mouse).  Set to true to
298      * allow drag and drop to start with any mouse click that is propogated
299      * by the browser
300      * @property primaryButtonOnly
301      * @type boolean
302      */
303     primaryButtonOnly: true,
304
305     /**
306      * The availabe property is false until the linked dom element is accessible.
307      * @property available
308      * @type boolean
309      */
310     available: false,
311
312     /**
313      * By default, drags can only be initiated if the mousedown occurs in the
314      * region the linked element is.  This is done in part to work around a
315      * bug in some browsers that mis-report the mousedown if the previous
316      * mouseup happened outside of the window.  This property is set to true
317      * if outer handles are defined.
318      *
319      * @property hasOuterHandles
320      * @type boolean
321      * @default false
322      */
323     hasOuterHandles: false,
324
325     /**
326      * Code that executes immediately before the startDrag event
327      * @method b4StartDrag
328      * @private
329      */
330     b4StartDrag: function(x, y) { },
331
332     /**
333      * Abstract method called after a drag/drop object is clicked
334      * and the drag or mousedown time thresholds have beeen met.
335      * @method startDrag
336      * @param {int} X click location
337      * @param {int} Y click location
338      */
339     startDrag: function(x, y) { /* override this */ },
340
341     /**
342      * Code that executes immediately before the onDrag event
343      * @method b4Drag
344      * @private
345      */
346     b4Drag: function(e) { },
347
348     /**
349      * Abstract method called during the onMouseMove event while dragging an
350      * object.
351      * @method onDrag
352      * @param {Event} e the mousemove event
353      */
354     onDrag: function(e) { /* override this */ },
355
356     /**
357      * Abstract method called when this element fist begins hovering over
358      * another DragDrop obj
359      * @method onDragEnter
360      * @param {Event} e the mousemove event
361      * @param {String|DragDrop[]} id In POINT mode, the element
362      * id this is hovering over.  In INTERSECT mode, an array of one or more
363      * dragdrop items being hovered over.
364      */
365     onDragEnter: function(e, id) { /* override this */ },
366
367     /**
368      * Code that executes immediately before the onDragOver event
369      * @method b4DragOver
370      * @private
371      */
372     b4DragOver: function(e) { },
373
374     /**
375      * Abstract method called when this element is hovering over another
376      * DragDrop obj
377      * @method onDragOver
378      * @param {Event} e the mousemove event
379      * @param {String|DragDrop[]} id In POINT mode, the element
380      * id this is hovering over.  In INTERSECT mode, an array of dd items
381      * being hovered over.
382      */
383     onDragOver: function(e, id) { /* override this */ },
384
385     /**
386      * Code that executes immediately before the onDragOut event
387      * @method b4DragOut
388      * @private
389      */
390     b4DragOut: function(e) { },
391
392     /**
393      * Abstract method called when we are no longer hovering over an element
394      * @method onDragOut
395      * @param {Event} e the mousemove event
396      * @param {String|DragDrop[]} id In POINT mode, the element
397      * id this was hovering over.  In INTERSECT mode, an array of dd items
398      * that the mouse is no longer over.
399      */
400     onDragOut: function(e, id) { /* override this */ },
401
402     /**
403      * Code that executes immediately before the onDragDrop event
404      * @method b4DragDrop
405      * @private
406      */
407     b4DragDrop: function(e) { },
408
409     /**
410      * Abstract method called when this item is dropped on another DragDrop
411      * obj
412      * @method onDragDrop
413      * @param {Event} e the mouseup event
414      * @param {String|DragDrop[]} id In POINT mode, the element
415      * id this was dropped on.  In INTERSECT mode, an array of dd items this
416      * was dropped on.
417      */
418     onDragDrop: function(e, id) { /* override this */ },
419
420     /**
421      * Abstract method called when this item is dropped on an area with no
422      * drop target
423      * @method onInvalidDrop
424      * @param {Event} e the mouseup event
425      */
426     onInvalidDrop: function(e) { /* override this */ },
427
428     /**
429      * Code that executes immediately before the endDrag event
430      * @method b4EndDrag
431      * @private
432      */
433     b4EndDrag: function(e) { },
434
435     /**
436      * Fired when we are done dragging the object
437      * @method endDrag
438      * @param {Event} e the mouseup event
439      */
440     endDrag: function(e) { /* override this */ },
441
442     /**
443      * Code executed immediately before the onMouseDown event
444      * @method b4MouseDown
445      * @param {Event} e the mousedown event
446      * @private
447      */
448     b4MouseDown: function(e) {  },
449
450     /**
451      * Event handler that fires when a drag/drop obj gets a mousedown
452      * @method onMouseDown
453      * @param {Event} e the mousedown event
454      */
455     onMouseDown: function(e) { /* override this */ },
456
457     /**
458      * Event handler that fires when a drag/drop obj gets a mouseup
459      * @method onMouseUp
460      * @param {Event} e the mouseup event
461      */
462     onMouseUp: function(e) { /* override this */ },
463
464     /**
465      * Override the onAvailable method to do what is needed after the initial
466      * position was determined.
467      * @method onAvailable
468      */
469     onAvailable: function () {
470     },
471
472     /*
473      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
474      * @type Object
475      */
476     defaultPadding : {left:0, right:0, top:0, bottom:0},
477
478     /*
479      * Initializes the drag drop object's constraints to restrict movement to a certain element.
480  *
481  * Usage:
482  <pre><code>
483  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
484                 { dragElId: "existingProxyDiv" });
485  dd.startDrag = function(){
486      this.constrainTo("parent-id");
487  };
488  </code></pre>
489  * Or you can initalize it using the {@link Roo.Element} object:
490  <pre><code>
491  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
492      startDrag : function(){
493          this.constrainTo("parent-id");
494      }
495  });
496  </code></pre>
497      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
498      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
499      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
500      * an object containing the sides to pad. For example: {right:10, bottom:10}
501      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
502      */
503     constrainTo : function(constrainTo, pad, inContent){
504         if(typeof pad == "number"){
505             pad = {left: pad, right:pad, top:pad, bottom:pad};
506         }
507         pad = pad || this.defaultPadding;
508         var b = Roo.get(this.getEl()).getBox();
509         var ce = Roo.get(constrainTo);
510         var s = ce.getScroll();
511         var c, cd = ce.dom;
512         if(cd == document.body){
513             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
514         }else{
515             xy = ce.getXY();
516             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
517         }
518
519
520         var topSpace = b.y - c.y;
521         var leftSpace = b.x - c.x;
522
523         this.resetConstraints();
524         this.setXConstraint(leftSpace - (pad.left||0), // left
525                 c.width - leftSpace - b.width - (pad.right||0) //right
526         );
527         this.setYConstraint(topSpace - (pad.top||0), //top
528                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
529         );
530     },
531
532     /**
533      * Returns a reference to the linked element
534      * @method getEl
535      * @return {HTMLElement} the html element
536      */
537     getEl: function() {
538         if (!this._domRef) {
539             this._domRef = Roo.getDom(this.id);
540         }
541
542         return this._domRef;
543     },
544
545     /**
546      * Returns a reference to the actual element to drag.  By default this is
547      * the same as the html element, but it can be assigned to another
548      * element. An example of this can be found in Roo.dd.DDProxy
549      * @method getDragEl
550      * @return {HTMLElement} the html element
551      */
552     getDragEl: function() {
553         return Roo.getDom(this.dragElId);
554     },
555
556     /**
557      * Sets up the DragDrop object.  Must be called in the constructor of any
558      * Roo.dd.DragDrop subclass
559      * @method init
560      * @param id the id of the linked element
561      * @param {String} sGroup the group of related items
562      * @param {object} config configuration attributes
563      */
564     init: function(id, sGroup, config) {
565         this.initTarget(id, sGroup, config);
566         Event.on(this.id, "mousedown", this.handleMouseDown, this);
567         // Event.on(this.id, "selectstart", Event.preventDefault);
568     },
569
570     /**
571      * Initializes Targeting functionality only... the object does not
572      * get a mousedown handler.
573      * @method initTarget
574      * @param id the id of the linked element
575      * @param {String} sGroup the group of related items
576      * @param {object} config configuration attributes
577      */
578     initTarget: function(id, sGroup, config) {
579
580         // configuration attributes
581         this.config = config || {};
582
583         // create a local reference to the drag and drop manager
584         this.DDM = Roo.dd.DDM;
585         // initialize the groups array
586         this.groups = {};
587
588         // assume that we have an element reference instead of an id if the
589         // parameter is not a string
590         if (typeof id !== "string") {
591             id = Roo.id(id);
592         }
593
594         // set the id
595         this.id = id;
596
597         // add to an interaction group
598         this.addToGroup((sGroup) ? sGroup : "default");
599
600         // We don't want to register this as the handle with the manager
601         // so we just set the id rather than calling the setter.
602         this.handleElId = id;
603
604         // the linked element is the element that gets dragged by default
605         this.setDragElId(id);
606
607         // by default, clicked anchors will not start drag operations.
608         this.invalidHandleTypes = { A: "A" };
609         this.invalidHandleIds = {};
610         this.invalidHandleClasses = [];
611
612         this.applyConfig();
613
614         this.handleOnAvailable();
615     },
616
617     /**
618      * Applies the configuration parameters that were passed into the constructor.
619      * This is supposed to happen at each level through the inheritance chain.  So
620      * a DDProxy implentation will execute apply config on DDProxy, DD, and
621      * DragDrop in order to get all of the parameters that are available in
622      * each object.
623      * @method applyConfig
624      */
625     applyConfig: function() {
626
627         // configurable properties:
628         //    padding, isTarget, maintainOffset, primaryButtonOnly
629         this.padding           = this.config.padding || [0, 0, 0, 0];
630         this.isTarget          = (this.config.isTarget !== false);
631         this.maintainOffset    = (this.config.maintainOffset);
632         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
633
634     },
635
636     /**
637      * Executed when the linked element is available
638      * @method handleOnAvailable
639      * @private
640      */
641     handleOnAvailable: function() {
642         this.available = true;
643         this.resetConstraints();
644         this.onAvailable();
645     },
646
647      /**
648      * Configures the padding for the target zone in px.  Effectively expands
649      * (or reduces) the virtual object size for targeting calculations.
650      * Supports css-style shorthand; if only one parameter is passed, all sides
651      * will have that padding, and if only two are passed, the top and bottom
652      * will have the first param, the left and right the second.
653      * @method setPadding
654      * @param {int} iTop    Top pad
655      * @param {int} iRight  Right pad
656      * @param {int} iBot    Bot pad
657      * @param {int} iLeft   Left pad
658      */
659     setPadding: function(iTop, iRight, iBot, iLeft) {
660         // this.padding = [iLeft, iRight, iTop, iBot];
661         if (!iRight && 0 !== iRight) {
662             this.padding = [iTop, iTop, iTop, iTop];
663         } else if (!iBot && 0 !== iBot) {
664             this.padding = [iTop, iRight, iTop, iRight];
665         } else {
666             this.padding = [iTop, iRight, iBot, iLeft];
667         }
668     },
669
670     /**
671      * Stores the initial placement of the linked element.
672      * @method setInitialPosition
673      * @param {int} diffX   the X offset, default 0
674      * @param {int} diffY   the Y offset, default 0
675      */
676     setInitPosition: function(diffX, diffY) {
677         var el = this.getEl();
678
679         if (!this.DDM.verifyEl(el)) {
680             return;
681         }
682
683         var dx = diffX || 0;
684         var dy = diffY || 0;
685
686         var p = Dom.getXY( el );
687
688         this.initPageX = p[0] - dx;
689         this.initPageY = p[1] - dy;
690
691         this.lastPageX = p[0];
692         this.lastPageY = p[1];
693
694
695         this.setStartPosition(p);
696     },
697
698     /**
699      * Sets the start position of the element.  This is set when the obj
700      * is initialized, the reset when a drag is started.
701      * @method setStartPosition
702      * @param pos current position (from previous lookup)
703      * @private
704      */
705     setStartPosition: function(pos) {
706         var p = pos || Dom.getXY( this.getEl() );
707         this.deltaSetXY = null;
708
709         this.startPageX = p[0];
710         this.startPageY = p[1];
711     },
712
713     /**
714      * Add this instance to a group of related drag/drop objects.  All
715      * instances belong to at least one group, and can belong to as many
716      * groups as needed.
717      * @method addToGroup
718      * @param sGroup {string} the name of the group
719      */
720     addToGroup: function(sGroup) {
721         this.groups[sGroup] = true;
722         this.DDM.regDragDrop(this, sGroup);
723     },
724
725     /**
726      * Remove's this instance from the supplied interaction group
727      * @method removeFromGroup
728      * @param {string}  sGroup  The group to drop
729      */
730     removeFromGroup: function(sGroup) {
731         if (this.groups[sGroup]) {
732             delete this.groups[sGroup];
733         }
734
735         this.DDM.removeDDFromGroup(this, sGroup);
736     },
737
738     /**
739      * Allows you to specify that an element other than the linked element
740      * will be moved with the cursor during a drag
741      * @method setDragElId
742      * @param id {string} the id of the element that will be used to initiate the drag
743      */
744     setDragElId: function(id) {
745         this.dragElId = id;
746     },
747
748     /**
749      * Allows you to specify a child of the linked element that should be
750      * used to initiate the drag operation.  An example of this would be if
751      * you have a content div with text and links.  Clicking anywhere in the
752      * content area would normally start the drag operation.  Use this method
753      * to specify that an element inside of the content div is the element
754      * that starts the drag operation.
755      * @method setHandleElId
756      * @param id {string} the id of the element that will be used to
757      * initiate the drag.
758      */
759     setHandleElId: function(id) {
760         if (typeof id !== "string") {
761             id = Roo.id(id);
762         }
763         this.handleElId = id;
764         this.DDM.regHandle(this.id, id);
765     },
766
767     /**
768      * Allows you to set an element outside of the linked element as a drag
769      * handle
770      * @method setOuterHandleElId
771      * @param id the id of the element that will be used to initiate the drag
772      */
773     setOuterHandleElId: function(id) {
774         if (typeof id !== "string") {
775             id = Roo.id(id);
776         }
777         Event.on(id, "mousedown",
778                 this.handleMouseDown, this);
779         this.setHandleElId(id);
780
781         this.hasOuterHandles = true;
782     },
783
784     /**
785      * Remove all drag and drop hooks for this element
786      * @method unreg
787      */
788     unreg: function() {
789         Event.un(this.id, "mousedown",
790                 this.handleMouseDown);
791         this._domRef = null;
792         this.DDM._remove(this);
793     },
794
795     destroy : function(){
796         this.unreg();
797     },
798
799     /**
800      * Returns true if this instance is locked, or the drag drop mgr is locked
801      * (meaning that all drag/drop is disabled on the page.)
802      * @method isLocked
803      * @return {boolean} true if this obj or all drag/drop is locked, else
804      * false
805      */
806     isLocked: function() {
807         return (this.DDM.isLocked() || this.locked);
808     },
809
810     /**
811      * Fired when this object is clicked
812      * @method handleMouseDown
813      * @param {Event} e
814      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
815      * @private
816      */
817     handleMouseDown: function(e, oDD){
818         if (this.primaryButtonOnly && e.button != 0) {
819             return;
820         }
821
822         if (this.isLocked()) {
823             return;
824         }
825
826         this.DDM.refreshCache(this.groups);
827
828         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
829         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
830         } else {
831             if (this.clickValidator(e)) {
832
833                 // set the initial element position
834                 this.setStartPosition();
835
836
837                 this.b4MouseDown(e);
838                 this.onMouseDown(e);
839
840                 this.DDM.handleMouseDown(e, this);
841
842                 this.DDM.stopEvent(e);
843             } else {
844
845
846             }
847         }
848     },
849
850     clickValidator: function(e) {
851         var target = e.getTarget();
852         return ( this.isValidHandleChild(target) &&
853                     (this.id == this.handleElId ||
854                         this.DDM.handleWasClicked(target, this.id)) );
855     },
856
857     /**
858      * Allows you to specify a tag name that should not start a drag operation
859      * when clicked.  This is designed to facilitate embedding links within a
860      * drag handle that do something other than start the drag.
861      * @method addInvalidHandleType
862      * @param {string} tagName the type of element to exclude
863      */
864     addInvalidHandleType: function(tagName) {
865         var type = tagName.toUpperCase();
866         this.invalidHandleTypes[type] = type;
867     },
868
869     /**
870      * Lets you to specify an element id for a child of a drag handle
871      * that should not initiate a drag
872      * @method addInvalidHandleId
873      * @param {string} id the element id of the element you wish to ignore
874      */
875     addInvalidHandleId: function(id) {
876         if (typeof id !== "string") {
877             id = Roo.id(id);
878         }
879         this.invalidHandleIds[id] = id;
880     },
881
882     /**
883      * Lets you specify a css class of elements that will not initiate a drag
884      * @method addInvalidHandleClass
885      * @param {string} cssClass the class of the elements you wish to ignore
886      */
887     addInvalidHandleClass: function(cssClass) {
888         this.invalidHandleClasses.push(cssClass);
889     },
890
891     /**
892      * Unsets an excluded tag name set by addInvalidHandleType
893      * @method removeInvalidHandleType
894      * @param {string} tagName the type of element to unexclude
895      */
896     removeInvalidHandleType: function(tagName) {
897         var type = tagName.toUpperCase();
898         // this.invalidHandleTypes[type] = null;
899         delete this.invalidHandleTypes[type];
900     },
901
902     /**
903      * Unsets an invalid handle id
904      * @method removeInvalidHandleId
905      * @param {string} id the id of the element to re-enable
906      */
907     removeInvalidHandleId: function(id) {
908         if (typeof id !== "string") {
909             id = Roo.id(id);
910         }
911         delete this.invalidHandleIds[id];
912     },
913
914     /**
915      * Unsets an invalid css class
916      * @method removeInvalidHandleClass
917      * @param {string} cssClass the class of the element(s) you wish to
918      * re-enable
919      */
920     removeInvalidHandleClass: function(cssClass) {
921         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
922             if (this.invalidHandleClasses[i] == cssClass) {
923                 delete this.invalidHandleClasses[i];
924             }
925         }
926     },
927
928     /**
929      * Checks the tag exclusion list to see if this click should be ignored
930      * @method isValidHandleChild
931      * @param {HTMLElement} node the HTMLElement to evaluate
932      * @return {boolean} true if this is a valid tag type, false if not
933      */
934     isValidHandleChild: function(node) {
935
936         var valid = true;
937         // var n = (node.nodeName == "#text") ? node.parentNode : node;
938         var nodeName;
939         try {
940             nodeName = node.nodeName.toUpperCase();
941         } catch(e) {
942             nodeName = node.nodeName;
943         }
944         valid = valid && !this.invalidHandleTypes[nodeName];
945         valid = valid && !this.invalidHandleIds[node.id];
946
947         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
948             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
949         }
950
951
952         return valid;
953
954     },
955
956     /**
957      * Create the array of horizontal tick marks if an interval was specified
958      * in setXConstraint().
959      * @method setXTicks
960      * @private
961      */
962     setXTicks: function(iStartX, iTickSize) {
963         this.xTicks = [];
964         this.xTickSize = iTickSize;
965
966         var tickMap = {};
967
968         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
969             if (!tickMap[i]) {
970                 this.xTicks[this.xTicks.length] = i;
971                 tickMap[i] = true;
972             }
973         }
974
975         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
976             if (!tickMap[i]) {
977                 this.xTicks[this.xTicks.length] = i;
978                 tickMap[i] = true;
979             }
980         }
981
982         this.xTicks.sort(this.DDM.numericSort) ;
983     },
984
985     /**
986      * Create the array of vertical tick marks if an interval was specified in
987      * setYConstraint().
988      * @method setYTicks
989      * @private
990      */
991     setYTicks: function(iStartY, iTickSize) {
992         this.yTicks = [];
993         this.yTickSize = iTickSize;
994
995         var tickMap = {};
996
997         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
998             if (!tickMap[i]) {
999                 this.yTicks[this.yTicks.length] = i;
1000                 tickMap[i] = true;
1001             }
1002         }
1003
1004         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1005             if (!tickMap[i]) {
1006                 this.yTicks[this.yTicks.length] = i;
1007                 tickMap[i] = true;
1008             }
1009         }
1010
1011         this.yTicks.sort(this.DDM.numericSort) ;
1012     },
1013
1014     /**
1015      * By default, the element can be dragged any place on the screen.  Use
1016      * this method to limit the horizontal travel of the element.  Pass in
1017      * 0,0 for the parameters if you want to lock the drag to the y axis.
1018      * @method setXConstraint
1019      * @param {int} iLeft the number of pixels the element can move to the left
1020      * @param {int} iRight the number of pixels the element can move to the
1021      * right
1022      * @param {int} iTickSize optional parameter for specifying that the
1023      * element
1024      * should move iTickSize pixels at a time.
1025      */
1026     setXConstraint: function(iLeft, iRight, iTickSize) {
1027         this.leftConstraint = iLeft;
1028         this.rightConstraint = iRight;
1029
1030         this.minX = this.initPageX - iLeft;
1031         this.maxX = this.initPageX + iRight;
1032         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1033
1034         this.constrainX = true;
1035     },
1036
1037     /**
1038      * Clears any constraints applied to this instance.  Also clears ticks
1039      * since they can't exist independent of a constraint at this time.
1040      * @method clearConstraints
1041      */
1042     clearConstraints: function() {
1043         this.constrainX = false;
1044         this.constrainY = false;
1045         this.clearTicks();
1046     },
1047
1048     /**
1049      * Clears any tick interval defined for this instance
1050      * @method clearTicks
1051      */
1052     clearTicks: function() {
1053         this.xTicks = null;
1054         this.yTicks = null;
1055         this.xTickSize = 0;
1056         this.yTickSize = 0;
1057     },
1058
1059     /**
1060      * By default, the element can be dragged any place on the screen.  Set
1061      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1062      * parameters if you want to lock the drag to the x axis.
1063      * @method setYConstraint
1064      * @param {int} iUp the number of pixels the element can move up
1065      * @param {int} iDown the number of pixels the element can move down
1066      * @param {int} iTickSize optional parameter for specifying that the
1067      * element should move iTickSize pixels at a time.
1068      */
1069     setYConstraint: function(iUp, iDown, iTickSize) {
1070         this.topConstraint = iUp;
1071         this.bottomConstraint = iDown;
1072
1073         this.minY = this.initPageY - iUp;
1074         this.maxY = this.initPageY + iDown;
1075         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1076
1077         this.constrainY = true;
1078
1079     },
1080
1081     /**
1082      * resetConstraints must be called if you manually reposition a dd element.
1083      * @method resetConstraints
1084      * @param {boolean} maintainOffset
1085      */
1086     resetConstraints: function() {
1087
1088
1089         // Maintain offsets if necessary
1090         if (this.initPageX || this.initPageX === 0) {
1091             // figure out how much this thing has moved
1092             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1093             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1094
1095             this.setInitPosition(dx, dy);
1096
1097         // This is the first time we have detected the element's position
1098         } else {
1099             this.setInitPosition();
1100         }
1101
1102         if (this.constrainX) {
1103             this.setXConstraint( this.leftConstraint,
1104                                  this.rightConstraint,
1105                                  this.xTickSize        );
1106         }
1107
1108         if (this.constrainY) {
1109             this.setYConstraint( this.topConstraint,
1110                                  this.bottomConstraint,
1111                                  this.yTickSize         );
1112         }
1113     },
1114
1115     /**
1116      * Normally the drag element is moved pixel by pixel, but we can specify
1117      * that it move a number of pixels at a time.  This method resolves the
1118      * location when we have it set up like this.
1119      * @method getTick
1120      * @param {int} val where we want to place the object
1121      * @param {int[]} tickArray sorted array of valid points
1122      * @return {int} the closest tick
1123      * @private
1124      */
1125     getTick: function(val, tickArray) {
1126
1127         if (!tickArray) {
1128             // If tick interval is not defined, it is effectively 1 pixel,
1129             // so we return the value passed to us.
1130             return val;
1131         } else if (tickArray[0] >= val) {
1132             // The value is lower than the first tick, so we return the first
1133             // tick.
1134             return tickArray[0];
1135         } else {
1136             for (var i=0, len=tickArray.length; i<len; ++i) {
1137                 var next = i + 1;
1138                 if (tickArray[next] && tickArray[next] >= val) {
1139                     var diff1 = val - tickArray[i];
1140                     var diff2 = tickArray[next] - val;
1141                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1142                 }
1143             }
1144
1145             // The value is larger than the last tick, so we return the last
1146             // tick.
1147             return tickArray[tickArray.length - 1];
1148         }
1149     },
1150
1151     /**
1152      * toString method
1153      * @method toString
1154      * @return {string} string representation of the dd obj
1155      */
1156     toString: function() {
1157         return ("DragDrop " + this.id);
1158     }
1159
1160 };
1161
1162 })();
1163 /*
1164  * Based on:
1165  * Ext JS Library 1.1.1
1166  * Copyright(c) 2006-2007, Ext JS, LLC.
1167  *
1168  * Originally Released Under LGPL - original licence link has changed is not relivant.
1169  *
1170  * Fork - LGPL
1171  * <script type="text/javascript">
1172  */
1173
1174
1175 /**
1176  * The drag and drop utility provides a framework for building drag and drop
1177  * applications.  In addition to enabling drag and drop for specific elements,
1178  * the drag and drop elements are tracked by the manager class, and the
1179  * interactions between the various elements are tracked during the drag and
1180  * the implementing code is notified about these important moments.
1181  */
1182
1183 // Only load the library once.  Rewriting the manager class would orphan
1184 // existing drag and drop instances.
1185 if (!Roo.dd.DragDropMgr) {
1186
1187 /**
1188  * @class Roo.dd.DragDropMgr
1189  * DragDropMgr is a singleton that tracks the element interaction for
1190  * all DragDrop items in the window.  Generally, you will not call
1191  * this class directly, but it does have helper methods that could
1192  * be useful in your DragDrop implementations.
1193  * @singleton
1194  */
1195 Roo.dd.DragDropMgr = function() {
1196
1197     var Event = Roo.EventManager;
1198
1199     return {
1200
1201         /**
1202          * Two dimensional Array of registered DragDrop objects.  The first
1203          * dimension is the DragDrop item group, the second the DragDrop
1204          * object.
1205          * @property ids
1206          * @type {string: string}
1207          * @private
1208          * @static
1209          */
1210         ids: {},
1211
1212         /**
1213          * Array of element ids defined as drag handles.  Used to determine
1214          * if the element that generated the mousedown event is actually the
1215          * handle and not the html element itself.
1216          * @property handleIds
1217          * @type {string: string}
1218          * @private
1219          * @static
1220          */
1221         handleIds: {},
1222
1223         /**
1224          * the DragDrop object that is currently being dragged
1225          * @property dragCurrent
1226          * @type DragDrop
1227          * @private
1228          * @static
1229          **/
1230         dragCurrent: null,
1231
1232         /**
1233          * the DragDrop object(s) that are being hovered over
1234          * @property dragOvers
1235          * @type Array
1236          * @private
1237          * @static
1238          */
1239         dragOvers: {},
1240
1241         /**
1242          * the X distance between the cursor and the object being dragged
1243          * @property deltaX
1244          * @type int
1245          * @private
1246          * @static
1247          */
1248         deltaX: 0,
1249
1250         /**
1251          * the Y distance between the cursor and the object being dragged
1252          * @property deltaY
1253          * @type int
1254          * @private
1255          * @static
1256          */
1257         deltaY: 0,
1258
1259         /**
1260          * Flag to determine if we should prevent the default behavior of the
1261          * events we define. By default this is true, but this can be set to
1262          * false if you need the default behavior (not recommended)
1263          * @property preventDefault
1264          * @type boolean
1265          * @static
1266          */
1267         preventDefault: true,
1268
1269         /**
1270          * Flag to determine if we should stop the propagation of the events
1271          * we generate. This is true by default but you may want to set it to
1272          * false if the html element contains other features that require the
1273          * mouse click.
1274          * @property stopPropagation
1275          * @type boolean
1276          * @static
1277          */
1278         stopPropagation: true,
1279
1280         /**
1281          * Internal flag that is set to true when drag and drop has been
1282          * intialized
1283          * @property initialized
1284          * @private
1285          * @static
1286          */
1287         initalized: false,
1288
1289         /**
1290          * All drag and drop can be disabled.
1291          * @property locked
1292          * @private
1293          * @static
1294          */
1295         locked: false,
1296
1297         /**
1298          * Called the first time an element is registered.
1299          * @method init
1300          * @private
1301          * @static
1302          */
1303         init: function() {
1304             this.initialized = true;
1305         },
1306
1307         /**
1308          * In point mode, drag and drop interaction is defined by the
1309          * location of the cursor during the drag/drop
1310          * @property POINT
1311          * @type int
1312          * @static
1313          */
1314         POINT: 0,
1315
1316         /**
1317          * In intersect mode, drag and drop interactio nis defined by the
1318          * overlap of two or more drag and drop objects.
1319          * @property INTERSECT
1320          * @type int
1321          * @static
1322          */
1323         INTERSECT: 1,
1324
1325         /**
1326          * The current drag and drop mode.  Default: POINT
1327          * @property mode
1328          * @type int
1329          * @static
1330          */
1331         mode: 0,
1332
1333         /**
1334          * Runs method on all drag and drop objects
1335          * @method _execOnAll
1336          * @private
1337          * @static
1338          */
1339         _execOnAll: function(sMethod, args) {
1340             for (var i in this.ids) {
1341                 for (var j in this.ids[i]) {
1342                     var oDD = this.ids[i][j];
1343                     if (! this.isTypeOfDD(oDD)) {
1344                         continue;
1345                     }
1346                     oDD[sMethod].apply(oDD, args);
1347                 }
1348             }
1349         },
1350
1351         /**
1352          * Drag and drop initialization.  Sets up the global event handlers
1353          * @method _onLoad
1354          * @private
1355          * @static
1356          */
1357         _onLoad: function() {
1358
1359             this.init();
1360
1361
1362             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1363             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1364             Event.on(window,   "unload",    this._onUnload, this, true);
1365             Event.on(window,   "resize",    this._onResize, this, true);
1366             // Event.on(window,   "mouseout",    this._test);
1367
1368         },
1369
1370         /**
1371          * Reset constraints on all drag and drop objs
1372          * @method _onResize
1373          * @private
1374          * @static
1375          */
1376         _onResize: function(e) {
1377             this._execOnAll("resetConstraints", []);
1378         },
1379
1380         /**
1381          * Lock all drag and drop functionality
1382          * @method lock
1383          * @static
1384          */
1385         lock: function() { this.locked = true; },
1386
1387         /**
1388          * Unlock all drag and drop functionality
1389          * @method unlock
1390          * @static
1391          */
1392         unlock: function() { this.locked = false; },
1393
1394         /**
1395          * Is drag and drop locked?
1396          * @method isLocked
1397          * @return {boolean} True if drag and drop is locked, false otherwise.
1398          * @static
1399          */
1400         isLocked: function() { return this.locked; },
1401
1402         /**
1403          * Location cache that is set for all drag drop objects when a drag is
1404          * initiated, cleared when the drag is finished.
1405          * @property locationCache
1406          * @private
1407          * @static
1408          */
1409         locationCache: {},
1410
1411         /**
1412          * Set useCache to false if you want to force object the lookup of each
1413          * drag and drop linked element constantly during a drag.
1414          * @property useCache
1415          * @type boolean
1416          * @static
1417          */
1418         useCache: true,
1419
1420         /**
1421          * The number of pixels that the mouse needs to move after the
1422          * mousedown before the drag is initiated.  Default=3;
1423          * @property clickPixelThresh
1424          * @type int
1425          * @static
1426          */
1427         clickPixelThresh: 3,
1428
1429         /**
1430          * The number of milliseconds after the mousedown event to initiate the
1431          * drag if we don't get a mouseup event. Default=1000
1432          * @property clickTimeThresh
1433          * @type int
1434          * @static
1435          */
1436         clickTimeThresh: 350,
1437
1438         /**
1439          * Flag that indicates that either the drag pixel threshold or the
1440          * mousdown time threshold has been met
1441          * @property dragThreshMet
1442          * @type boolean
1443          * @private
1444          * @static
1445          */
1446         dragThreshMet: false,
1447
1448         /**
1449          * Timeout used for the click time threshold
1450          * @property clickTimeout
1451          * @type Object
1452          * @private
1453          * @static
1454          */
1455         clickTimeout: null,
1456
1457         /**
1458          * The X position of the mousedown event stored for later use when a
1459          * drag threshold is met.
1460          * @property startX
1461          * @type int
1462          * @private
1463          * @static
1464          */
1465         startX: 0,
1466
1467         /**
1468          * The Y position of the mousedown event stored for later use when a
1469          * drag threshold is met.
1470          * @property startY
1471          * @type int
1472          * @private
1473          * @static
1474          */
1475         startY: 0,
1476
1477         /**
1478          * Each DragDrop instance must be registered with the DragDropMgr.
1479          * This is executed in DragDrop.init()
1480          * @method regDragDrop
1481          * @param {DragDrop} oDD the DragDrop object to register
1482          * @param {String} sGroup the name of the group this element belongs to
1483          * @static
1484          */
1485         regDragDrop: function(oDD, sGroup) {
1486             if (!this.initialized) { this.init(); }
1487
1488             if (!this.ids[sGroup]) {
1489                 this.ids[sGroup] = {};
1490             }
1491             this.ids[sGroup][oDD.id] = oDD;
1492         },
1493
1494         /**
1495          * Removes the supplied dd instance from the supplied group. Executed
1496          * by DragDrop.removeFromGroup, so don't call this function directly.
1497          * @method removeDDFromGroup
1498          * @private
1499          * @static
1500          */
1501         removeDDFromGroup: function(oDD, sGroup) {
1502             if (!this.ids[sGroup]) {
1503                 this.ids[sGroup] = {};
1504             }
1505
1506             var obj = this.ids[sGroup];
1507             if (obj && obj[oDD.id]) {
1508                 delete obj[oDD.id];
1509             }
1510         },
1511
1512         /**
1513          * Unregisters a drag and drop item.  This is executed in
1514          * DragDrop.unreg, use that method instead of calling this directly.
1515          * @method _remove
1516          * @private
1517          * @static
1518          */
1519         _remove: function(oDD) {
1520             for (var g in oDD.groups) {
1521                 if (g && this.ids[g][oDD.id]) {
1522                     delete this.ids[g][oDD.id];
1523                 }
1524             }
1525             delete this.handleIds[oDD.id];
1526         },
1527
1528         /**
1529          * Each DragDrop handle element must be registered.  This is done
1530          * automatically when executing DragDrop.setHandleElId()
1531          * @method regHandle
1532          * @param {String} sDDId the DragDrop id this element is a handle for
1533          * @param {String} sHandleId the id of the element that is the drag
1534          * handle
1535          * @static
1536          */
1537         regHandle: function(sDDId, sHandleId) {
1538             if (!this.handleIds[sDDId]) {
1539                 this.handleIds[sDDId] = {};
1540             }
1541             this.handleIds[sDDId][sHandleId] = sHandleId;
1542         },
1543
1544         /**
1545          * Utility function to determine if a given element has been
1546          * registered as a drag drop item.
1547          * @method isDragDrop
1548          * @param {String} id the element id to check
1549          * @return {boolean} true if this element is a DragDrop item,
1550          * false otherwise
1551          * @static
1552          */
1553         isDragDrop: function(id) {
1554             return ( this.getDDById(id) ) ? true : false;
1555         },
1556
1557         /**
1558          * Returns the drag and drop instances that are in all groups the
1559          * passed in instance belongs to.
1560          * @method getRelated
1561          * @param {DragDrop} p_oDD the obj to get related data for
1562          * @param {boolean} bTargetsOnly if true, only return targetable objs
1563          * @return {DragDrop[]} the related instances
1564          * @static
1565          */
1566         getRelated: function(p_oDD, bTargetsOnly) {
1567             var oDDs = [];
1568             for (var i in p_oDD.groups) {
1569                 for (j in this.ids[i]) {
1570                     var dd = this.ids[i][j];
1571                     if (! this.isTypeOfDD(dd)) {
1572                         continue;
1573                     }
1574                     if (!bTargetsOnly || dd.isTarget) {
1575                         oDDs[oDDs.length] = dd;
1576                     }
1577                 }
1578             }
1579
1580             return oDDs;
1581         },
1582
1583         /**
1584          * Returns true if the specified dd target is a legal target for
1585          * the specifice drag obj
1586          * @method isLegalTarget
1587          * @param {DragDrop} the drag obj
1588          * @param {DragDrop} the target
1589          * @return {boolean} true if the target is a legal target for the
1590          * dd obj
1591          * @static
1592          */
1593         isLegalTarget: function (oDD, oTargetDD) {
1594             var targets = this.getRelated(oDD, true);
1595             for (var i=0, len=targets.length;i<len;++i) {
1596                 if (targets[i].id == oTargetDD.id) {
1597                     return true;
1598                 }
1599             }
1600
1601             return false;
1602         },
1603
1604         /**
1605          * My goal is to be able to transparently determine if an object is
1606          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1607          * returns "object", oDD.constructor.toString() always returns
1608          * "DragDrop" and not the name of the subclass.  So for now it just
1609          * evaluates a well-known variable in DragDrop.
1610          * @method isTypeOfDD
1611          * @param {Object} the object to evaluate
1612          * @return {boolean} true if typeof oDD = DragDrop
1613          * @static
1614          */
1615         isTypeOfDD: function (oDD) {
1616             return (oDD && oDD.__ygDragDrop);
1617         },
1618
1619         /**
1620          * Utility function to determine if a given element has been
1621          * registered as a drag drop handle for the given Drag Drop object.
1622          * @method isHandle
1623          * @param {String} id the element id to check
1624          * @return {boolean} true if this element is a DragDrop handle, false
1625          * otherwise
1626          * @static
1627          */
1628         isHandle: function(sDDId, sHandleId) {
1629             return ( this.handleIds[sDDId] &&
1630                             this.handleIds[sDDId][sHandleId] );
1631         },
1632
1633         /**
1634          * Returns the DragDrop instance for a given id
1635          * @method getDDById
1636          * @param {String} id the id of the DragDrop object
1637          * @return {DragDrop} the drag drop object, null if it is not found
1638          * @static
1639          */
1640         getDDById: function(id) {
1641             for (var i in this.ids) {
1642                 if (this.ids[i][id]) {
1643                     return this.ids[i][id];
1644                 }
1645             }
1646             return null;
1647         },
1648
1649         /**
1650          * Fired after a registered DragDrop object gets the mousedown event.
1651          * Sets up the events required to track the object being dragged
1652          * @method handleMouseDown
1653          * @param {Event} e the event
1654          * @param oDD the DragDrop object being dragged
1655          * @private
1656          * @static
1657          */
1658         handleMouseDown: function(e, oDD) {
1659             if(Roo.QuickTips){
1660                 Roo.QuickTips.disable();
1661             }
1662             this.currentTarget = e.getTarget();
1663
1664             this.dragCurrent = oDD;
1665
1666             var el = oDD.getEl();
1667
1668             // track start position
1669             this.startX = e.getPageX();
1670             this.startY = e.getPageY();
1671
1672             this.deltaX = this.startX - el.offsetLeft;
1673             this.deltaY = this.startY - el.offsetTop;
1674
1675             this.dragThreshMet = false;
1676
1677             this.clickTimeout = setTimeout(
1678                     function() {
1679                         var DDM = Roo.dd.DDM;
1680                         DDM.startDrag(DDM.startX, DDM.startY);
1681                     },
1682                     this.clickTimeThresh );
1683         },
1684
1685         /**
1686          * Fired when either the drag pixel threshol or the mousedown hold
1687          * time threshold has been met.
1688          * @method startDrag
1689          * @param x {int} the X position of the original mousedown
1690          * @param y {int} the Y position of the original mousedown
1691          * @static
1692          */
1693         startDrag: function(x, y) {
1694             clearTimeout(this.clickTimeout);
1695             if (this.dragCurrent) {
1696                 this.dragCurrent.b4StartDrag(x, y);
1697                 this.dragCurrent.startDrag(x, y);
1698             }
1699             this.dragThreshMet = true;
1700         },
1701
1702         /**
1703          * Internal function to handle the mouseup event.  Will be invoked
1704          * from the context of the document.
1705          * @method handleMouseUp
1706          * @param {Event} e the event
1707          * @private
1708          * @static
1709          */
1710         handleMouseUp: function(e) {
1711
1712             if(Roo.QuickTips){
1713                 Roo.QuickTips.enable();
1714             }
1715             if (! this.dragCurrent) {
1716                 return;
1717             }
1718
1719             clearTimeout(this.clickTimeout);
1720
1721             if (this.dragThreshMet) {
1722                 this.fireEvents(e, true);
1723             } else {
1724             }
1725
1726             this.stopDrag(e);
1727
1728             this.stopEvent(e);
1729         },
1730
1731         /**
1732          * Utility to stop event propagation and event default, if these
1733          * features are turned on.
1734          * @method stopEvent
1735          * @param {Event} e the event as returned by this.getEvent()
1736          * @static
1737          */
1738         stopEvent: function(e){
1739             if(this.stopPropagation) {
1740                 e.stopPropagation();
1741             }
1742
1743             if (this.preventDefault) {
1744                 e.preventDefault();
1745             }
1746         },
1747
1748         /**
1749          * Internal function to clean up event handlers after the drag
1750          * operation is complete
1751          * @method stopDrag
1752          * @param {Event} e the event
1753          * @private
1754          * @static
1755          */
1756         stopDrag: function(e) {
1757             // Fire the drag end event for the item that was dragged
1758             if (this.dragCurrent) {
1759                 if (this.dragThreshMet) {
1760                     this.dragCurrent.b4EndDrag(e);
1761                     this.dragCurrent.endDrag(e);
1762                 }
1763
1764                 this.dragCurrent.onMouseUp(e);
1765             }
1766
1767             this.dragCurrent = null;
1768             this.dragOvers = {};
1769         },
1770
1771         /**
1772          * Internal function to handle the mousemove event.  Will be invoked
1773          * from the context of the html element.
1774          *
1775          * @TODO figure out what we can do about mouse events lost when the
1776          * user drags objects beyond the window boundary.  Currently we can
1777          * detect this in internet explorer by verifying that the mouse is
1778          * down during the mousemove event.  Firefox doesn't give us the
1779          * button state on the mousemove event.
1780          * @method handleMouseMove
1781          * @param {Event} e the event
1782          * @private
1783          * @static
1784          */
1785         handleMouseMove: function(e) {
1786             if (! this.dragCurrent) {
1787                 return true;
1788             }
1789
1790             // var button = e.which || e.button;
1791
1792             // check for IE mouseup outside of page boundary
1793             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1794                 this.stopEvent(e);
1795                 return this.handleMouseUp(e);
1796             }
1797
1798             if (!this.dragThreshMet) {
1799                 var diffX = Math.abs(this.startX - e.getPageX());
1800                 var diffY = Math.abs(this.startY - e.getPageY());
1801                 if (diffX > this.clickPixelThresh ||
1802                             diffY > this.clickPixelThresh) {
1803                     this.startDrag(this.startX, this.startY);
1804                 }
1805             }
1806
1807             if (this.dragThreshMet) {
1808                 this.dragCurrent.b4Drag(e);
1809                 this.dragCurrent.onDrag(e);
1810                 if(!this.dragCurrent.moveOnly){
1811                     this.fireEvents(e, false);
1812                 }
1813             }
1814
1815             this.stopEvent(e);
1816
1817             return true;
1818         },
1819
1820         /**
1821          * Iterates over all of the DragDrop elements to find ones we are
1822          * hovering over or dropping on
1823          * @method fireEvents
1824          * @param {Event} e the event
1825          * @param {boolean} isDrop is this a drop op or a mouseover op?
1826          * @private
1827          * @static
1828          */
1829         fireEvents: function(e, isDrop) {
1830             var dc = this.dragCurrent;
1831
1832             // If the user did the mouse up outside of the window, we could
1833             // get here even though we have ended the drag.
1834             if (!dc || dc.isLocked()) {
1835                 return;
1836             }
1837
1838             var pt = e.getPoint();
1839
1840             // cache the previous dragOver array
1841             var oldOvers = [];
1842
1843             var outEvts   = [];
1844             var overEvts  = [];
1845             var dropEvts  = [];
1846             var enterEvts = [];
1847
1848             // Check to see if the object(s) we were hovering over is no longer
1849             // being hovered over so we can fire the onDragOut event
1850             for (var i in this.dragOvers) {
1851
1852                 var ddo = this.dragOvers[i];
1853
1854                 if (! this.isTypeOfDD(ddo)) {
1855                     continue;
1856                 }
1857
1858                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1859                     outEvts.push( ddo );
1860                 }
1861
1862                 oldOvers[i] = true;
1863                 delete this.dragOvers[i];
1864             }
1865
1866             for (var sGroup in dc.groups) {
1867
1868                 if ("string" != typeof sGroup) {
1869                     continue;
1870                 }
1871
1872                 for (i in this.ids[sGroup]) {
1873                     var oDD = this.ids[sGroup][i];
1874                     if (! this.isTypeOfDD(oDD)) {
1875                         continue;
1876                     }
1877
1878                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1879                         if (this.isOverTarget(pt, oDD, this.mode)) {
1880                             // look for drop interactions
1881                             if (isDrop) {
1882                                 dropEvts.push( oDD );
1883                             // look for drag enter and drag over interactions
1884                             } else {
1885
1886                                 // initial drag over: dragEnter fires
1887                                 if (!oldOvers[oDD.id]) {
1888                                     enterEvts.push( oDD );
1889                                 // subsequent drag overs: dragOver fires
1890                                 } else {
1891                                     overEvts.push( oDD );
1892                                 }
1893
1894                                 this.dragOvers[oDD.id] = oDD;
1895                             }
1896                         }
1897                     }
1898                 }
1899             }
1900
1901             if (this.mode) {
1902                 if (outEvts.length) {
1903                     dc.b4DragOut(e, outEvts);
1904                     dc.onDragOut(e, outEvts);
1905                 }
1906
1907                 if (enterEvts.length) {
1908                     dc.onDragEnter(e, enterEvts);
1909                 }
1910
1911                 if (overEvts.length) {
1912                     dc.b4DragOver(e, overEvts);
1913                     dc.onDragOver(e, overEvts);
1914                 }
1915
1916                 if (dropEvts.length) {
1917                     dc.b4DragDrop(e, dropEvts);
1918                     dc.onDragDrop(e, dropEvts);
1919                 }
1920
1921             } else {
1922                 // fire dragout events
1923                 var len = 0;
1924                 for (i=0, len=outEvts.length; i<len; ++i) {
1925                     dc.b4DragOut(e, outEvts[i].id);
1926                     dc.onDragOut(e, outEvts[i].id);
1927                 }
1928
1929                 // fire enter events
1930                 for (i=0,len=enterEvts.length; i<len; ++i) {
1931                     // dc.b4DragEnter(e, oDD.id);
1932                     dc.onDragEnter(e, enterEvts[i].id);
1933                 }
1934
1935                 // fire over events
1936                 for (i=0,len=overEvts.length; i<len; ++i) {
1937                     dc.b4DragOver(e, overEvts[i].id);
1938                     dc.onDragOver(e, overEvts[i].id);
1939                 }
1940
1941                 // fire drop events
1942                 for (i=0, len=dropEvts.length; i<len; ++i) {
1943                     dc.b4DragDrop(e, dropEvts[i].id);
1944                     dc.onDragDrop(e, dropEvts[i].id);
1945                 }
1946
1947             }
1948
1949             // notify about a drop that did not find a target
1950             if (isDrop && !dropEvts.length) {
1951                 dc.onInvalidDrop(e);
1952             }
1953
1954         },
1955
1956         /**
1957          * Helper function for getting the best match from the list of drag
1958          * and drop objects returned by the drag and drop events when we are
1959          * in INTERSECT mode.  It returns either the first object that the
1960          * cursor is over, or the object that has the greatest overlap with
1961          * the dragged element.
1962          * @method getBestMatch
1963          * @param  {DragDrop[]} dds The array of drag and drop objects
1964          * targeted
1965          * @return {DragDrop}       The best single match
1966          * @static
1967          */
1968         getBestMatch: function(dds) {
1969             var winner = null;
1970             // Return null if the input is not what we expect
1971             //if (!dds || !dds.length || dds.length == 0) {
1972                // winner = null;
1973             // If there is only one item, it wins
1974             //} else if (dds.length == 1) {
1975
1976             var len = dds.length;
1977
1978             if (len == 1) {
1979                 winner = dds[0];
1980             } else {
1981                 // Loop through the targeted items
1982                 for (var i=0; i<len; ++i) {
1983                     var dd = dds[i];
1984                     // If the cursor is over the object, it wins.  If the
1985                     // cursor is over multiple matches, the first one we come
1986                     // to wins.
1987                     if (dd.cursorIsOver) {
1988                         winner = dd;
1989                         break;
1990                     // Otherwise the object with the most overlap wins
1991                     } else {
1992                         if (!winner ||
1993                             winner.overlap.getArea() < dd.overlap.getArea()) {
1994                             winner = dd;
1995                         }
1996                     }
1997                 }
1998             }
1999
2000             return winner;
2001         },
2002
2003         /**
2004          * Refreshes the cache of the top-left and bottom-right points of the
2005          * drag and drop objects in the specified group(s).  This is in the
2006          * format that is stored in the drag and drop instance, so typical
2007          * usage is:
2008          * <code>
2009          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2010          * </code>
2011          * Alternatively:
2012          * <code>
2013          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2014          * </code>
2015          * @TODO this really should be an indexed array.  Alternatively this
2016          * method could accept both.
2017          * @method refreshCache
2018          * @param {Object} groups an associative array of groups to refresh
2019          * @static
2020          */
2021         refreshCache: function(groups) {
2022             for (var sGroup in groups) {
2023                 if ("string" != typeof sGroup) {
2024                     continue;
2025                 }
2026                 for (var i in this.ids[sGroup]) {
2027                     var oDD = this.ids[sGroup][i];
2028
2029                     if (this.isTypeOfDD(oDD)) {
2030                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2031                         var loc = this.getLocation(oDD);
2032                         if (loc) {
2033                             this.locationCache[oDD.id] = loc;
2034                         } else {
2035                             delete this.locationCache[oDD.id];
2036                             // this will unregister the drag and drop object if
2037                             // the element is not in a usable state
2038                             // oDD.unreg();
2039                         }
2040                     }
2041                 }
2042             }
2043         },
2044
2045         /**
2046          * This checks to make sure an element exists and is in the DOM.  The
2047          * main purpose is to handle cases where innerHTML is used to remove
2048          * drag and drop objects from the DOM.  IE provides an 'unspecified
2049          * error' when trying to access the offsetParent of such an element
2050          * @method verifyEl
2051          * @param {HTMLElement} el the element to check
2052          * @return {boolean} true if the element looks usable
2053          * @static
2054          */
2055         verifyEl: function(el) {
2056             if (el) {
2057                 var parent;
2058                 if(Roo.isIE){
2059                     try{
2060                         parent = el.offsetParent;
2061                     }catch(e){}
2062                 }else{
2063                     parent = el.offsetParent;
2064                 }
2065                 if (parent) {
2066                     return true;
2067                 }
2068             }
2069
2070             return false;
2071         },
2072
2073         /**
2074          * Returns a Region object containing the drag and drop element's position
2075          * and size, including the padding configured for it
2076          * @method getLocation
2077          * @param {DragDrop} oDD the drag and drop object to get the
2078          *                       location for
2079          * @return {Roo.lib.Region} a Region object representing the total area
2080          *                             the element occupies, including any padding
2081          *                             the instance is configured for.
2082          * @static
2083          */
2084         getLocation: function(oDD) {
2085             if (! this.isTypeOfDD(oDD)) {
2086                 return null;
2087             }
2088
2089             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2090
2091             try {
2092                 pos= Roo.lib.Dom.getXY(el);
2093             } catch (e) { }
2094
2095             if (!pos) {
2096                 return null;
2097             }
2098
2099             x1 = pos[0];
2100             x2 = x1 + el.offsetWidth;
2101             y1 = pos[1];
2102             y2 = y1 + el.offsetHeight;
2103
2104             t = y1 - oDD.padding[0];
2105             r = x2 + oDD.padding[1];
2106             b = y2 + oDD.padding[2];
2107             l = x1 - oDD.padding[3];
2108
2109             return new Roo.lib.Region( t, r, b, l );
2110         },
2111
2112         /**
2113          * Checks the cursor location to see if it over the target
2114          * @method isOverTarget
2115          * @param {Roo.lib.Point} pt The point to evaluate
2116          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2117          * @return {boolean} true if the mouse is over the target
2118          * @private
2119          * @static
2120          */
2121         isOverTarget: function(pt, oTarget, intersect) {
2122             // use cache if available
2123             var loc = this.locationCache[oTarget.id];
2124             if (!loc || !this.useCache) {
2125                 loc = this.getLocation(oTarget);
2126                 this.locationCache[oTarget.id] = loc;
2127
2128             }
2129
2130             if (!loc) {
2131                 return false;
2132             }
2133
2134             oTarget.cursorIsOver = loc.contains( pt );
2135
2136             // DragDrop is using this as a sanity check for the initial mousedown
2137             // in this case we are done.  In POINT mode, if the drag obj has no
2138             // contraints, we are also done. Otherwise we need to evaluate the
2139             // location of the target as related to the actual location of the
2140             // dragged element.
2141             var dc = this.dragCurrent;
2142             if (!dc || !dc.getTargetCoord ||
2143                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2144                 return oTarget.cursorIsOver;
2145             }
2146
2147             oTarget.overlap = null;
2148
2149             // Get the current location of the drag element, this is the
2150             // location of the mouse event less the delta that represents
2151             // where the original mousedown happened on the element.  We
2152             // need to consider constraints and ticks as well.
2153             var pos = dc.getTargetCoord(pt.x, pt.y);
2154
2155             var el = dc.getDragEl();
2156             var curRegion = new Roo.lib.Region( pos.y,
2157                                                    pos.x + el.offsetWidth,
2158                                                    pos.y + el.offsetHeight,
2159                                                    pos.x );
2160
2161             var overlap = curRegion.intersect(loc);
2162
2163             if (overlap) {
2164                 oTarget.overlap = overlap;
2165                 return (intersect) ? true : oTarget.cursorIsOver;
2166             } else {
2167                 return false;
2168             }
2169         },
2170
2171         /**
2172          * unload event handler
2173          * @method _onUnload
2174          * @private
2175          * @static
2176          */
2177         _onUnload: function(e, me) {
2178             Roo.dd.DragDropMgr.unregAll();
2179         },
2180
2181         /**
2182          * Cleans up the drag and drop events and objects.
2183          * @method unregAll
2184          * @private
2185          * @static
2186          */
2187         unregAll: function() {
2188
2189             if (this.dragCurrent) {
2190                 this.stopDrag();
2191                 this.dragCurrent = null;
2192             }
2193
2194             this._execOnAll("unreg", []);
2195
2196             for (i in this.elementCache) {
2197                 delete this.elementCache[i];
2198             }
2199
2200             this.elementCache = {};
2201             this.ids = {};
2202         },
2203
2204         /**
2205          * A cache of DOM elements
2206          * @property elementCache
2207          * @private
2208          * @static
2209          */
2210         elementCache: {},
2211
2212         /**
2213          * Get the wrapper for the DOM element specified
2214          * @method getElWrapper
2215          * @param {String} id the id of the element to get
2216          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2217          * @private
2218          * @deprecated This wrapper isn't that useful
2219          * @static
2220          */
2221         getElWrapper: function(id) {
2222             var oWrapper = this.elementCache[id];
2223             if (!oWrapper || !oWrapper.el) {
2224                 oWrapper = this.elementCache[id] =
2225                     new this.ElementWrapper(Roo.getDom(id));
2226             }
2227             return oWrapper;
2228         },
2229
2230         /**
2231          * Returns the actual DOM element
2232          * @method getElement
2233          * @param {String} id the id of the elment to get
2234          * @return {Object} The element
2235          * @deprecated use Roo.getDom instead
2236          * @static
2237          */
2238         getElement: function(id) {
2239             return Roo.getDom(id);
2240         },
2241
2242         /**
2243          * Returns the style property for the DOM element (i.e.,
2244          * document.getElById(id).style)
2245          * @method getCss
2246          * @param {String} id the id of the elment to get
2247          * @return {Object} The style property of the element
2248          * @deprecated use Roo.getDom instead
2249          * @static
2250          */
2251         getCss: function(id) {
2252             var el = Roo.getDom(id);
2253             return (el) ? el.style : null;
2254         },
2255
2256         /**
2257          * Inner class for cached elements
2258          * @class DragDropMgr.ElementWrapper
2259          * @for DragDropMgr
2260          * @private
2261          * @deprecated
2262          */
2263         ElementWrapper: function(el) {
2264                 /**
2265                  * The element
2266                  * @property el
2267                  */
2268                 this.el = el || null;
2269                 /**
2270                  * The element id
2271                  * @property id
2272                  */
2273                 this.id = this.el && el.id;
2274                 /**
2275                  * A reference to the style property
2276                  * @property css
2277                  */
2278                 this.css = this.el && el.style;
2279             },
2280
2281         /**
2282          * Returns the X position of an html element
2283          * @method getPosX
2284          * @param el the element for which to get the position
2285          * @return {int} the X coordinate
2286          * @for DragDropMgr
2287          * @deprecated use Roo.lib.Dom.getX instead
2288          * @static
2289          */
2290         getPosX: function(el) {
2291             return Roo.lib.Dom.getX(el);
2292         },
2293
2294         /**
2295          * Returns the Y position of an html element
2296          * @method getPosY
2297          * @param el the element for which to get the position
2298          * @return {int} the Y coordinate
2299          * @deprecated use Roo.lib.Dom.getY instead
2300          * @static
2301          */
2302         getPosY: function(el) {
2303             return Roo.lib.Dom.getY(el);
2304         },
2305
2306         /**
2307          * Swap two nodes.  In IE, we use the native method, for others we
2308          * emulate the IE behavior
2309          * @method swapNode
2310          * @param n1 the first node to swap
2311          * @param n2 the other node to swap
2312          * @static
2313          */
2314         swapNode: function(n1, n2) {
2315             if (n1.swapNode) {
2316                 n1.swapNode(n2);
2317             } else {
2318                 var p = n2.parentNode;
2319                 var s = n2.nextSibling;
2320
2321                 if (s == n1) {
2322                     p.insertBefore(n1, n2);
2323                 } else if (n2 == n1.nextSibling) {
2324                     p.insertBefore(n2, n1);
2325                 } else {
2326                     n1.parentNode.replaceChild(n2, n1);
2327                     p.insertBefore(n1, s);
2328                 }
2329             }
2330         },
2331
2332         /**
2333          * Returns the current scroll position
2334          * @method getScroll
2335          * @private
2336          * @static
2337          */
2338         getScroll: function () {
2339             var t, l, dde=document.documentElement, db=document.body;
2340             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2341                 t = dde.scrollTop;
2342                 l = dde.scrollLeft;
2343             } else if (db) {
2344                 t = db.scrollTop;
2345                 l = db.scrollLeft;
2346             } else {
2347
2348             }
2349             return { top: t, left: l };
2350         },
2351
2352         /**
2353          * Returns the specified element style property
2354          * @method getStyle
2355          * @param {HTMLElement} el          the element
2356          * @param {string}      styleProp   the style property
2357          * @return {string} The value of the style property
2358          * @deprecated use Roo.lib.Dom.getStyle
2359          * @static
2360          */
2361         getStyle: function(el, styleProp) {
2362             return Roo.fly(el).getStyle(styleProp);
2363         },
2364
2365         /**
2366          * Gets the scrollTop
2367          * @method getScrollTop
2368          * @return {int} the document's scrollTop
2369          * @static
2370          */
2371         getScrollTop: function () { return this.getScroll().top; },
2372
2373         /**
2374          * Gets the scrollLeft
2375          * @method getScrollLeft
2376          * @return {int} the document's scrollTop
2377          * @static
2378          */
2379         getScrollLeft: function () { return this.getScroll().left; },
2380
2381         /**
2382          * Sets the x/y position of an element to the location of the
2383          * target element.
2384          * @method moveToEl
2385          * @param {HTMLElement} moveEl      The element to move
2386          * @param {HTMLElement} targetEl    The position reference element
2387          * @static
2388          */
2389         moveToEl: function (moveEl, targetEl) {
2390             var aCoord = Roo.lib.Dom.getXY(targetEl);
2391             Roo.lib.Dom.setXY(moveEl, aCoord);
2392         },
2393
2394         /**
2395          * Numeric array sort function
2396          * @method numericSort
2397          * @static
2398          */
2399         numericSort: function(a, b) { return (a - b); },
2400
2401         /**
2402          * Internal counter
2403          * @property _timeoutCount
2404          * @private
2405          * @static
2406          */
2407         _timeoutCount: 0,
2408
2409         /**
2410          * Trying to make the load order less important.  Without this we get
2411          * an error if this file is loaded before the Event Utility.
2412          * @method _addListeners
2413          * @private
2414          * @static
2415          */
2416         _addListeners: function() {
2417             var DDM = Roo.dd.DDM;
2418             if ( Roo.lib.Event && document ) {
2419                 DDM._onLoad();
2420             } else {
2421                 if (DDM._timeoutCount > 2000) {
2422                 } else {
2423                     setTimeout(DDM._addListeners, 10);
2424                     if (document && document.body) {
2425                         DDM._timeoutCount += 1;
2426                     }
2427                 }
2428             }
2429         },
2430
2431         /**
2432          * Recursively searches the immediate parent and all child nodes for
2433          * the handle element in order to determine wheter or not it was
2434          * clicked.
2435          * @method handleWasClicked
2436          * @param node the html element to inspect
2437          * @static
2438          */
2439         handleWasClicked: function(node, id) {
2440             if (this.isHandle(id, node.id)) {
2441                 return true;
2442             } else {
2443                 // check to see if this is a text node child of the one we want
2444                 var p = node.parentNode;
2445
2446                 while (p) {
2447                     if (this.isHandle(id, p.id)) {
2448                         return true;
2449                     } else {
2450                         p = p.parentNode;
2451                     }
2452                 }
2453             }
2454
2455             return false;
2456         }
2457
2458     };
2459
2460 }();
2461
2462 // shorter alias, save a few bytes
2463 Roo.dd.DDM = Roo.dd.DragDropMgr;
2464 Roo.dd.DDM._addListeners();
2465
2466 }/*
2467  * Based on:
2468  * Ext JS Library 1.1.1
2469  * Copyright(c) 2006-2007, Ext JS, LLC.
2470  *
2471  * Originally Released Under LGPL - original licence link has changed is not relivant.
2472  *
2473  * Fork - LGPL
2474  * <script type="text/javascript">
2475  */
2476
2477 /**
2478  * @class Roo.dd.DD
2479  * A DragDrop implementation where the linked element follows the
2480  * mouse cursor during a drag.
2481  * @extends Roo.dd.DragDrop
2482  * @constructor
2483  * @param {String} id the id of the linked element
2484  * @param {String} sGroup the group of related DragDrop items
2485  * @param {object} config an object containing configurable attributes
2486  *                Valid properties for DD:
2487  *                    scroll
2488  */
2489 Roo.dd.DD = function(id, sGroup, config) {
2490     if (id) {
2491         this.init(id, sGroup, config);
2492     }
2493 };
2494
2495 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2496
2497     /**
2498      * When set to true, the utility automatically tries to scroll the browser
2499      * window wehn a drag and drop element is dragged near the viewport boundary.
2500      * Defaults to true.
2501      * @property scroll
2502      * @type boolean
2503      */
2504     scroll: true,
2505
2506     /**
2507      * Sets the pointer offset to the distance between the linked element's top
2508      * left corner and the location the element was clicked
2509      * @method autoOffset
2510      * @param {int} iPageX the X coordinate of the click
2511      * @param {int} iPageY the Y coordinate of the click
2512      */
2513     autoOffset: function(iPageX, iPageY) {
2514         var x = iPageX - this.startPageX;
2515         var y = iPageY - this.startPageY;
2516         this.setDelta(x, y);
2517     },
2518
2519     /**
2520      * Sets the pointer offset.  You can call this directly to force the
2521      * offset to be in a particular location (e.g., pass in 0,0 to set it
2522      * to the center of the object)
2523      * @method setDelta
2524      * @param {int} iDeltaX the distance from the left
2525      * @param {int} iDeltaY the distance from the top
2526      */
2527     setDelta: function(iDeltaX, iDeltaY) {
2528         this.deltaX = iDeltaX;
2529         this.deltaY = iDeltaY;
2530     },
2531
2532     /**
2533      * Sets the drag element to the location of the mousedown or click event,
2534      * maintaining the cursor location relative to the location on the element
2535      * that was clicked.  Override this if you want to place the element in a
2536      * location other than where the cursor is.
2537      * @method setDragElPos
2538      * @param {int} iPageX the X coordinate of the mousedown or drag event
2539      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2540      */
2541     setDragElPos: function(iPageX, iPageY) {
2542         // the first time we do this, we are going to check to make sure
2543         // the element has css positioning
2544
2545         var el = this.getDragEl();
2546         this.alignElWithMouse(el, iPageX, iPageY);
2547     },
2548
2549     /**
2550      * Sets the element to the location of the mousedown or click event,
2551      * maintaining the cursor location relative to the location on the element
2552      * that was clicked.  Override this if you want to place the element in a
2553      * location other than where the cursor is.
2554      * @method alignElWithMouse
2555      * @param {HTMLElement} el the element to move
2556      * @param {int} iPageX the X coordinate of the mousedown or drag event
2557      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2558      */
2559     alignElWithMouse: function(el, iPageX, iPageY) {
2560         var oCoord = this.getTargetCoord(iPageX, iPageY);
2561         var fly = el.dom ? el : Roo.fly(el);
2562         if (!this.deltaSetXY) {
2563             var aCoord = [oCoord.x, oCoord.y];
2564             fly.setXY(aCoord);
2565             var newLeft = fly.getLeft(true);
2566             var newTop  = fly.getTop(true);
2567             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2568         } else {
2569             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2570         }
2571
2572         this.cachePosition(oCoord.x, oCoord.y);
2573         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2574         return oCoord;
2575     },
2576
2577     /**
2578      * Saves the most recent position so that we can reset the constraints and
2579      * tick marks on-demand.  We need to know this so that we can calculate the
2580      * number of pixels the element is offset from its original position.
2581      * @method cachePosition
2582      * @param iPageX the current x position (optional, this just makes it so we
2583      * don't have to look it up again)
2584      * @param iPageY the current y position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      */
2587     cachePosition: function(iPageX, iPageY) {
2588         if (iPageX) {
2589             this.lastPageX = iPageX;
2590             this.lastPageY = iPageY;
2591         } else {
2592             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2593             this.lastPageX = aCoord[0];
2594             this.lastPageY = aCoord[1];
2595         }
2596     },
2597
2598     /**
2599      * Auto-scroll the window if the dragged object has been moved beyond the
2600      * visible window boundary.
2601      * @method autoScroll
2602      * @param {int} x the drag element's x position
2603      * @param {int} y the drag element's y position
2604      * @param {int} h the height of the drag element
2605      * @param {int} w the width of the drag element
2606      * @private
2607      */
2608     autoScroll: function(x, y, h, w) {
2609
2610         if (this.scroll) {
2611             // The client height
2612             var clientH = Roo.lib.Dom.getViewWidth();
2613
2614             // The client width
2615             var clientW = Roo.lib.Dom.getViewHeight();
2616
2617             // The amt scrolled down
2618             var st = this.DDM.getScrollTop();
2619
2620             // The amt scrolled right
2621             var sl = this.DDM.getScrollLeft();
2622
2623             // Location of the bottom of the element
2624             var bot = h + y;
2625
2626             // Location of the right of the element
2627             var right = w + x;
2628
2629             // The distance from the cursor to the bottom of the visible area,
2630             // adjusted so that we don't scroll if the cursor is beyond the
2631             // element drag constraints
2632             var toBot = (clientH + st - y - this.deltaY);
2633
2634             // The distance from the cursor to the right of the visible area
2635             var toRight = (clientW + sl - x - this.deltaX);
2636
2637
2638             // How close to the edge the cursor must be before we scroll
2639             // var thresh = (document.all) ? 100 : 40;
2640             var thresh = 40;
2641
2642             // How many pixels to scroll per autoscroll op.  This helps to reduce
2643             // clunky scrolling. IE is more sensitive about this ... it needs this
2644             // value to be higher.
2645             var scrAmt = (document.all) ? 80 : 30;
2646
2647             // Scroll down if we are near the bottom of the visible page and the
2648             // obj extends below the crease
2649             if ( bot > clientH && toBot < thresh ) {
2650                 window.scrollTo(sl, st + scrAmt);
2651             }
2652
2653             // Scroll up if the window is scrolled down and the top of the object
2654             // goes above the top border
2655             if ( y < st && st > 0 && y - st < thresh ) {
2656                 window.scrollTo(sl, st - scrAmt);
2657             }
2658
2659             // Scroll right if the obj is beyond the right border and the cursor is
2660             // near the border.
2661             if ( right > clientW && toRight < thresh ) {
2662                 window.scrollTo(sl + scrAmt, st);
2663             }
2664
2665             // Scroll left if the window has been scrolled to the right and the obj
2666             // extends past the left border
2667             if ( x < sl && sl > 0 && x - sl < thresh ) {
2668                 window.scrollTo(sl - scrAmt, st);
2669             }
2670         }
2671     },
2672
2673     /**
2674      * Finds the location the element should be placed if we want to move
2675      * it to where the mouse location less the click offset would place us.
2676      * @method getTargetCoord
2677      * @param {int} iPageX the X coordinate of the click
2678      * @param {int} iPageY the Y coordinate of the click
2679      * @return an object that contains the coordinates (Object.x and Object.y)
2680      * @private
2681      */
2682     getTargetCoord: function(iPageX, iPageY) {
2683
2684
2685         var x = iPageX - this.deltaX;
2686         var y = iPageY - this.deltaY;
2687
2688         if (this.constrainX) {
2689             if (x < this.minX) { x = this.minX; }
2690             if (x > this.maxX) { x = this.maxX; }
2691         }
2692
2693         if (this.constrainY) {
2694             if (y < this.minY) { y = this.minY; }
2695             if (y > this.maxY) { y = this.maxY; }
2696         }
2697
2698         x = this.getTick(x, this.xTicks);
2699         y = this.getTick(y, this.yTicks);
2700
2701
2702         return {x:x, y:y};
2703     },
2704
2705     /*
2706      * Sets up config options specific to this class. Overrides
2707      * Roo.dd.DragDrop, but all versions of this method through the
2708      * inheritance chain are called
2709      */
2710     applyConfig: function() {
2711         Roo.dd.DD.superclass.applyConfig.call(this);
2712         this.scroll = (this.config.scroll !== false);
2713     },
2714
2715     /*
2716      * Event that fires prior to the onMouseDown event.  Overrides
2717      * Roo.dd.DragDrop.
2718      */
2719     b4MouseDown: function(e) {
2720         // this.resetConstraints();
2721         this.autoOffset(e.getPageX(),
2722                             e.getPageY());
2723     },
2724
2725     /*
2726      * Event that fires prior to the onDrag event.  Overrides
2727      * Roo.dd.DragDrop.
2728      */
2729     b4Drag: function(e) {
2730         this.setDragElPos(e.getPageX(),
2731                             e.getPageY());
2732     },
2733
2734     toString: function() {
2735         return ("DD " + this.id);
2736     }
2737
2738     //////////////////////////////////////////////////////////////////////////
2739     // Debugging ygDragDrop events that can be overridden
2740     //////////////////////////////////////////////////////////////////////////
2741     /*
2742     startDrag: function(x, y) {
2743     },
2744
2745     onDrag: function(e) {
2746     },
2747
2748     onDragEnter: function(e, id) {
2749     },
2750
2751     onDragOver: function(e, id) {
2752     },
2753
2754     onDragOut: function(e, id) {
2755     },
2756
2757     onDragDrop: function(e, id) {
2758     },
2759
2760     endDrag: function(e) {
2761     }
2762
2763     */
2764
2765 });/*
2766  * Based on:
2767  * Ext JS Library 1.1.1
2768  * Copyright(c) 2006-2007, Ext JS, LLC.
2769  *
2770  * Originally Released Under LGPL - original licence link has changed is not relivant.
2771  *
2772  * Fork - LGPL
2773  * <script type="text/javascript">
2774  */
2775
2776 /**
2777  * @class Roo.dd.DDProxy
2778  * A DragDrop implementation that inserts an empty, bordered div into
2779  * the document that follows the cursor during drag operations.  At the time of
2780  * the click, the frame div is resized to the dimensions of the linked html
2781  * element, and moved to the exact location of the linked element.
2782  *
2783  * References to the "frame" element refer to the single proxy element that
2784  * was created to be dragged in place of all DDProxy elements on the
2785  * page.
2786  *
2787  * @extends Roo.dd.DD
2788  * @constructor
2789  * @param {String} id the id of the linked html element
2790  * @param {String} sGroup the group of related DragDrop objects
2791  * @param {object} config an object containing configurable attributes
2792  *                Valid properties for DDProxy in addition to those in DragDrop:
2793  *                   resizeFrame, centerFrame, dragElId
2794  */
2795 Roo.dd.DDProxy = function(id, sGroup, config) {
2796     if (id) {
2797         this.init(id, sGroup, config);
2798         this.initFrame();
2799     }
2800 };
2801
2802 /**
2803  * The default drag frame div id
2804  * @property Roo.dd.DDProxy.dragElId
2805  * @type String
2806  * @static
2807  */
2808 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2809
2810 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2811
2812     /**
2813      * By default we resize the drag frame to be the same size as the element
2814      * we want to drag (this is to get the frame effect).  We can turn it off
2815      * if we want a different behavior.
2816      * @property resizeFrame
2817      * @type boolean
2818      */
2819     resizeFrame: true,
2820
2821     /**
2822      * By default the frame is positioned exactly where the drag element is, so
2823      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2824      * you do not have constraints on the obj is to have the drag frame centered
2825      * around the cursor.  Set centerFrame to true for this effect.
2826      * @property centerFrame
2827      * @type boolean
2828      */
2829     centerFrame: false,
2830
2831     /**
2832      * Creates the proxy element if it does not yet exist
2833      * @method createFrame
2834      */
2835     createFrame: function() {
2836         var self = this;
2837         var body = document.body;
2838
2839         if (!body || !body.firstChild) {
2840             setTimeout( function() { self.createFrame(); }, 50 );
2841             return;
2842         }
2843
2844         var div = this.getDragEl();
2845
2846         if (!div) {
2847             div    = document.createElement("div");
2848             div.id = this.dragElId;
2849             var s  = div.style;
2850
2851             s.position   = "absolute";
2852             s.visibility = "hidden";
2853             s.cursor     = "move";
2854             s.border     = "2px solid #aaa";
2855             s.zIndex     = 999;
2856
2857             // appendChild can blow up IE if invoked prior to the window load event
2858             // while rendering a table.  It is possible there are other scenarios
2859             // that would cause this to happen as well.
2860             body.insertBefore(div, body.firstChild);
2861         }
2862     },
2863
2864     /**
2865      * Initialization for the drag frame element.  Must be called in the
2866      * constructor of all subclasses
2867      * @method initFrame
2868      */
2869     initFrame: function() {
2870         this.createFrame();
2871     },
2872
2873     applyConfig: function() {
2874         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2875
2876         this.resizeFrame = (this.config.resizeFrame !== false);
2877         this.centerFrame = (this.config.centerFrame);
2878         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2879     },
2880
2881     /**
2882      * Resizes the drag frame to the dimensions of the clicked object, positions
2883      * it over the object, and finally displays it
2884      * @method showFrame
2885      * @param {int} iPageX X click position
2886      * @param {int} iPageY Y click position
2887      * @private
2888      */
2889     showFrame: function(iPageX, iPageY) {
2890         var el = this.getEl();
2891         var dragEl = this.getDragEl();
2892         var s = dragEl.style;
2893
2894         this._resizeProxy();
2895
2896         if (this.centerFrame) {
2897             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2898                            Math.round(parseInt(s.height, 10)/2) );
2899         }
2900
2901         this.setDragElPos(iPageX, iPageY);
2902
2903         Roo.fly(dragEl).show();
2904     },
2905
2906     /**
2907      * The proxy is automatically resized to the dimensions of the linked
2908      * element when a drag is initiated, unless resizeFrame is set to false
2909      * @method _resizeProxy
2910      * @private
2911      */
2912     _resizeProxy: function() {
2913         if (this.resizeFrame) {
2914             var el = this.getEl();
2915             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2916         }
2917     },
2918
2919     // overrides Roo.dd.DragDrop
2920     b4MouseDown: function(e) {
2921         var x = e.getPageX();
2922         var y = e.getPageY();
2923         this.autoOffset(x, y);
2924         this.setDragElPos(x, y);
2925     },
2926
2927     // overrides Roo.dd.DragDrop
2928     b4StartDrag: function(x, y) {
2929         // show the drag frame
2930         this.showFrame(x, y);
2931     },
2932
2933     // overrides Roo.dd.DragDrop
2934     b4EndDrag: function(e) {
2935         Roo.fly(this.getDragEl()).hide();
2936     },
2937
2938     // overrides Roo.dd.DragDrop
2939     // By default we try to move the element to the last location of the frame.
2940     // This is so that the default behavior mirrors that of Roo.dd.DD.
2941     endDrag: function(e) {
2942
2943         var lel = this.getEl();
2944         var del = this.getDragEl();
2945
2946         // Show the drag frame briefly so we can get its position
2947         del.style.visibility = "";
2948
2949         this.beforeMove();
2950         // Hide the linked element before the move to get around a Safari
2951         // rendering bug.
2952         lel.style.visibility = "hidden";
2953         Roo.dd.DDM.moveToEl(lel, del);
2954         del.style.visibility = "hidden";
2955         lel.style.visibility = "";
2956
2957         this.afterDrag();
2958     },
2959
2960     beforeMove : function(){
2961
2962     },
2963
2964     afterDrag : function(){
2965
2966     },
2967
2968     toString: function() {
2969         return ("DDProxy " + this.id);
2970     }
2971
2972 });
2973 /*
2974  * Based on:
2975  * Ext JS Library 1.1.1
2976  * Copyright(c) 2006-2007, Ext JS, LLC.
2977  *
2978  * Originally Released Under LGPL - original licence link has changed is not relivant.
2979  *
2980  * Fork - LGPL
2981  * <script type="text/javascript">
2982  */
2983
2984  /**
2985  * @class Roo.dd.DDTarget
2986  * A DragDrop implementation that does not move, but can be a drop
2987  * target.  You would get the same result by simply omitting implementation
2988  * for the event callbacks, but this way we reduce the processing cost of the
2989  * event listener and the callbacks.
2990  * @extends Roo.dd.DragDrop
2991  * @constructor
2992  * @param {String} id the id of the element that is a drop target
2993  * @param {String} sGroup the group of related DragDrop objects
2994  * @param {object} config an object containing configurable attributes
2995  *                 Valid properties for DDTarget in addition to those in
2996  *                 DragDrop:
2997  *                    none
2998  */
2999 Roo.dd.DDTarget = function(id, sGroup, config) {
3000     if (id) {
3001         this.initTarget(id, sGroup, config);
3002     }
3003 };
3004
3005 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3006 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3007     toString: function() {
3008         return ("DDTarget " + this.id);
3009     }
3010 });
3011 /*
3012  * Based on:
3013  * Ext JS Library 1.1.1
3014  * Copyright(c) 2006-2007, Ext JS, LLC.
3015  *
3016  * Originally Released Under LGPL - original licence link has changed is not relivant.
3017  *
3018  * Fork - LGPL
3019  * <script type="text/javascript">
3020  */
3021  
3022
3023 /**
3024  * @class Roo.dd.ScrollManager
3025  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3026  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3027  * @singleton
3028  */
3029 Roo.dd.ScrollManager = function(){
3030     var ddm = Roo.dd.DragDropMgr;
3031     var els = {};
3032     var dragEl = null;
3033     var proc = {};
3034     
3035     var onStop = function(e){
3036         dragEl = null;
3037         clearProc();
3038     };
3039     
3040     var triggerRefresh = function(){
3041         if(ddm.dragCurrent){
3042              ddm.refreshCache(ddm.dragCurrent.groups);
3043         }
3044     };
3045     
3046     var doScroll = function(){
3047         if(ddm.dragCurrent){
3048             var dds = Roo.dd.ScrollManager;
3049             if(!dds.animate){
3050                 if(proc.el.scroll(proc.dir, dds.increment)){
3051                     triggerRefresh();
3052                 }
3053             }else{
3054                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3055             }
3056         }
3057     };
3058     
3059     var clearProc = function(){
3060         if(proc.id){
3061             clearInterval(proc.id);
3062         }
3063         proc.id = 0;
3064         proc.el = null;
3065         proc.dir = "";
3066     };
3067     
3068     var startProc = function(el, dir){
3069         clearProc();
3070         proc.el = el;
3071         proc.dir = dir;
3072         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3073     };
3074     
3075     var onFire = function(e, isDrop){
3076         if(isDrop || !ddm.dragCurrent){ return; }
3077         var dds = Roo.dd.ScrollManager;
3078         if(!dragEl || dragEl != ddm.dragCurrent){
3079             dragEl = ddm.dragCurrent;
3080             // refresh regions on drag start
3081             dds.refreshCache();
3082         }
3083         
3084         var xy = Roo.lib.Event.getXY(e);
3085         var pt = new Roo.lib.Point(xy[0], xy[1]);
3086         for(var id in els){
3087             var el = els[id], r = el._region;
3088             if(r && r.contains(pt) && el.isScrollable()){
3089                 if(r.bottom - pt.y <= dds.thresh){
3090                     if(proc.el != el){
3091                         startProc(el, "down");
3092                     }
3093                     return;
3094                 }else if(r.right - pt.x <= dds.thresh){
3095                     if(proc.el != el){
3096                         startProc(el, "left");
3097                     }
3098                     return;
3099                 }else if(pt.y - r.top <= dds.thresh){
3100                     if(proc.el != el){
3101                         startProc(el, "up");
3102                     }
3103                     return;
3104                 }else if(pt.x - r.left <= dds.thresh){
3105                     if(proc.el != el){
3106                         startProc(el, "right");
3107                     }
3108                     return;
3109                 }
3110             }
3111         }
3112         clearProc();
3113     };
3114     
3115     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3116     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3117     
3118     return {
3119         /**
3120          * Registers new overflow element(s) to auto scroll
3121          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3122          */
3123         register : function(el){
3124             if(el instanceof Array){
3125                 for(var i = 0, len = el.length; i < len; i++) {
3126                         this.register(el[i]);
3127                 }
3128             }else{
3129                 el = Roo.get(el);
3130                 els[el.id] = el;
3131             }
3132         },
3133         
3134         /**
3135          * Unregisters overflow element(s) so they are no longer scrolled
3136          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3137          */
3138         unregister : function(el){
3139             if(el instanceof Array){
3140                 for(var i = 0, len = el.length; i < len; i++) {
3141                         this.unregister(el[i]);
3142                 }
3143             }else{
3144                 el = Roo.get(el);
3145                 delete els[el.id];
3146             }
3147         },
3148         
3149         /**
3150          * The number of pixels from the edge of a container the pointer needs to be to 
3151          * trigger scrolling (defaults to 25)
3152          * @type Number
3153          */
3154         thresh : 25,
3155         
3156         /**
3157          * The number of pixels to scroll in each scroll increment (defaults to 50)
3158          * @type Number
3159          */
3160         increment : 100,
3161         
3162         /**
3163          * The frequency of scrolls in milliseconds (defaults to 500)
3164          * @type Number
3165          */
3166         frequency : 500,
3167         
3168         /**
3169          * True to animate the scroll (defaults to true)
3170          * @type Boolean
3171          */
3172         animate: true,
3173         
3174         /**
3175          * The animation duration in seconds - 
3176          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3177          * @type Number
3178          */
3179         animDuration: .4,
3180         
3181         /**
3182          * Manually trigger a cache refresh.
3183          */
3184         refreshCache : function(){
3185             for(var id in els){
3186                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3187                     els[id]._region = els[id].getRegion();
3188                 }
3189             }
3190         }
3191     };
3192 }();/*
3193  * Based on:
3194  * Ext JS Library 1.1.1
3195  * Copyright(c) 2006-2007, Ext JS, LLC.
3196  *
3197  * Originally Released Under LGPL - original licence link has changed is not relivant.
3198  *
3199  * Fork - LGPL
3200  * <script type="text/javascript">
3201  */
3202  
3203
3204 /**
3205  * @class Roo.dd.Registry
3206  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3207  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3208  * @singleton
3209  */
3210 Roo.dd.Registry = function(){
3211     var elements = {}; 
3212     var handles = {}; 
3213     var autoIdSeed = 0;
3214
3215     var getId = function(el, autogen){
3216         if(typeof el == "string"){
3217             return el;
3218         }
3219         var id = el.id;
3220         if(!id && autogen !== false){
3221             id = "roodd-" + (++autoIdSeed);
3222             el.id = id;
3223         }
3224         return id;
3225     };
3226     
3227     return {
3228     /**
3229      * Register a drag drop element
3230      * @param {String|HTMLElement} element The id or DOM node to register
3231      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3232      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3233      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3234      * populated in the data object (if applicable):
3235      * <pre>
3236 Value      Description<br />
3237 ---------  ------------------------------------------<br />
3238 handles    Array of DOM nodes that trigger dragging<br />
3239            for the element being registered<br />
3240 isHandle   True if the element passed in triggers<br />
3241            dragging itself, else false
3242 </pre>
3243      */
3244         register : function(el, data){
3245             data = data || {};
3246             if(typeof el == "string"){
3247                 el = document.getElementById(el);
3248             }
3249             data.ddel = el;
3250             elements[getId(el)] = data;
3251             if(data.isHandle !== false){
3252                 handles[data.ddel.id] = data;
3253             }
3254             if(data.handles){
3255                 var hs = data.handles;
3256                 for(var i = 0, len = hs.length; i < len; i++){
3257                         handles[getId(hs[i])] = data;
3258                 }
3259             }
3260         },
3261
3262     /**
3263      * Unregister a drag drop element
3264      * @param {String|HTMLElement}  element The id or DOM node to unregister
3265      */
3266         unregister : function(el){
3267             var id = getId(el, false);
3268             var data = elements[id];
3269             if(data){
3270                 delete elements[id];
3271                 if(data.handles){
3272                     var hs = data.handles;
3273                     for(var i = 0, len = hs.length; i < len; i++){
3274                         delete handles[getId(hs[i], false)];
3275                     }
3276                 }
3277             }
3278         },
3279
3280     /**
3281      * Returns the handle registered for a DOM Node by id
3282      * @param {String|HTMLElement} id The DOM node or id to look up
3283      * @return {Object} handle The custom handle data
3284      */
3285         getHandle : function(id){
3286             if(typeof id != "string"){ // must be element?
3287                 id = id.id;
3288             }
3289             return handles[id];
3290         },
3291
3292     /**
3293      * Returns the handle that is registered for the DOM node that is the target of the event
3294      * @param {Event} e The event
3295      * @return {Object} handle The custom handle data
3296      */
3297         getHandleFromEvent : function(e){
3298             var t = Roo.lib.Event.getTarget(e);
3299             return t ? handles[t.id] : null;
3300         },
3301
3302     /**
3303      * Returns a custom data object that is registered for a DOM node by id
3304      * @param {String|HTMLElement} id The DOM node or id to look up
3305      * @return {Object} data The custom data
3306      */
3307         getTarget : function(id){
3308             if(typeof id != "string"){ // must be element?
3309                 id = id.id;
3310             }
3311             return elements[id];
3312         },
3313
3314     /**
3315      * Returns a custom data object that is registered for the DOM node that is the target of the event
3316      * @param {Event} e The event
3317      * @return {Object} data The custom data
3318      */
3319         getTargetFromEvent : function(e){
3320             var t = Roo.lib.Event.getTarget(e);
3321             return t ? elements[t.id] || handles[t.id] : null;
3322         }
3323     };
3324 }();/*
3325  * Based on:
3326  * Ext JS Library 1.1.1
3327  * Copyright(c) 2006-2007, Ext JS, LLC.
3328  *
3329  * Originally Released Under LGPL - original licence link has changed is not relivant.
3330  *
3331  * Fork - LGPL
3332  * <script type="text/javascript">
3333  */
3334  
3335
3336 /**
3337  * @class Roo.dd.StatusProxy
3338  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3339  * default drag proxy used by all Roo.dd components.
3340  * @constructor
3341  * @param {Object} config
3342  */
3343 Roo.dd.StatusProxy = function(config){
3344     Roo.apply(this, config);
3345     this.id = this.id || Roo.id();
3346     this.el = new Roo.Layer({
3347         dh: {
3348             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3349                 {tag: "div", cls: "x-dd-drop-icon"},
3350                 {tag: "div", cls: "x-dd-drag-ghost"}
3351             ]
3352         }, 
3353         shadow: !config || config.shadow !== false
3354     });
3355     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3356     this.dropStatus = this.dropNotAllowed;
3357 };
3358
3359 Roo.dd.StatusProxy.prototype = {
3360     /**
3361      * @cfg {String} dropAllowed
3362      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3363      */
3364     dropAllowed : "x-dd-drop-ok",
3365     /**
3366      * @cfg {String} dropNotAllowed
3367      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3368      */
3369     dropNotAllowed : "x-dd-drop-nodrop",
3370
3371     /**
3372      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3373      * over the current target element.
3374      * @param {String} cssClass The css class for the new drop status indicator image
3375      */
3376     setStatus : function(cssClass){
3377         cssClass = cssClass || this.dropNotAllowed;
3378         if(this.dropStatus != cssClass){
3379             this.el.replaceClass(this.dropStatus, cssClass);
3380             this.dropStatus = cssClass;
3381         }
3382     },
3383
3384     /**
3385      * Resets the status indicator to the default dropNotAllowed value
3386      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3387      */
3388     reset : function(clearGhost){
3389         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3390         this.dropStatus = this.dropNotAllowed;
3391         if(clearGhost){
3392             this.ghost.update("");
3393         }
3394     },
3395
3396     /**
3397      * Updates the contents of the ghost element
3398      * @param {String} html The html that will replace the current innerHTML of the ghost element
3399      */
3400     update : function(html){
3401         if(typeof html == "string"){
3402             this.ghost.update(html);
3403         }else{
3404             this.ghost.update("");
3405             html.style.margin = "0";
3406             this.ghost.dom.appendChild(html);
3407         }
3408         // ensure float = none set?? cant remember why though.
3409         var el = this.ghost.dom.firstChild;
3410                 if(el){
3411                         Roo.fly(el).setStyle('float', 'none');
3412                 }
3413     },
3414     
3415     /**
3416      * Returns the underlying proxy {@link Roo.Layer}
3417      * @return {Roo.Layer} el
3418     */
3419     getEl : function(){
3420         return this.el;
3421     },
3422
3423     /**
3424      * Returns the ghost element
3425      * @return {Roo.Element} el
3426      */
3427     getGhost : function(){
3428         return this.ghost;
3429     },
3430
3431     /**
3432      * Hides the proxy
3433      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3434      */
3435     hide : function(clear){
3436         this.el.hide();
3437         if(clear){
3438             this.reset(true);
3439         }
3440     },
3441
3442     /**
3443      * Stops the repair animation if it's currently running
3444      */
3445     stop : function(){
3446         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3447             this.anim.stop();
3448         }
3449     },
3450
3451     /**
3452      * Displays this proxy
3453      */
3454     show : function(){
3455         this.el.show();
3456     },
3457
3458     /**
3459      * Force the Layer to sync its shadow and shim positions to the element
3460      */
3461     sync : function(){
3462         this.el.sync();
3463     },
3464
3465     /**
3466      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3467      * invalid drop operation by the item being dragged.
3468      * @param {Array} xy The XY position of the element ([x, y])
3469      * @param {Function} callback The function to call after the repair is complete
3470      * @param {Object} scope The scope in which to execute the callback
3471      */
3472     repair : function(xy, callback, scope){
3473         this.callback = callback;
3474         this.scope = scope;
3475         if(xy && this.animRepair !== false){
3476             this.el.addClass("x-dd-drag-repair");
3477             this.el.hideUnders(true);
3478             this.anim = this.el.shift({
3479                 duration: this.repairDuration || .5,
3480                 easing: 'easeOut',
3481                 xy: xy,
3482                 stopFx: true,
3483                 callback: this.afterRepair,
3484                 scope: this
3485             });
3486         }else{
3487             this.afterRepair();
3488         }
3489     },
3490
3491     // private
3492     afterRepair : function(){
3493         this.hide(true);
3494         if(typeof this.callback == "function"){
3495             this.callback.call(this.scope || this);
3496         }
3497         this.callback = null;
3498         this.scope = null;
3499     }
3500 };/*
3501  * Based on:
3502  * Ext JS Library 1.1.1
3503  * Copyright(c) 2006-2007, Ext JS, LLC.
3504  *
3505  * Originally Released Under LGPL - original licence link has changed is not relivant.
3506  *
3507  * Fork - LGPL
3508  * <script type="text/javascript">
3509  */
3510
3511 /**
3512  * @class Roo.dd.DragSource
3513  * @extends Roo.dd.DDProxy
3514  * A simple class that provides the basic implementation needed to make any element draggable.
3515  * @constructor
3516  * @param {String/HTMLElement/Element} el The container element
3517  * @param {Object} config
3518  */
3519 Roo.dd.DragSource = function(el, config){
3520     this.el = Roo.get(el);
3521     this.dragData = {};
3522     
3523     Roo.apply(this, config);
3524     
3525     if(!this.proxy){
3526         this.proxy = new Roo.dd.StatusProxy();
3527     }
3528
3529     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3530           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3531     
3532     this.dragging = false;
3533 };
3534
3535 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3536     /**
3537      * @cfg {String} dropAllowed
3538      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3539      */
3540     dropAllowed : "x-dd-drop-ok",
3541     /**
3542      * @cfg {String} dropNotAllowed
3543      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3544      */
3545     dropNotAllowed : "x-dd-drop-nodrop",
3546
3547     /**
3548      * Returns the data object associated with this drag source
3549      * @return {Object} data An object containing arbitrary data
3550      */
3551     getDragData : function(e){
3552         return this.dragData;
3553     },
3554
3555     // private
3556     onDragEnter : function(e, id){
3557         var target = Roo.dd.DragDropMgr.getDDById(id);
3558         this.cachedTarget = target;
3559         if(this.beforeDragEnter(target, e, id) !== false){
3560             if(target.isNotifyTarget){
3561                 var status = target.notifyEnter(this, e, this.dragData);
3562                 this.proxy.setStatus(status);
3563             }else{
3564                 this.proxy.setStatus(this.dropAllowed);
3565             }
3566             
3567             if(this.afterDragEnter){
3568                 /**
3569                  * An empty function by default, but provided so that you can perform a custom action
3570                  * when the dragged item enters the drop target by providing an implementation.
3571                  * @param {Roo.dd.DragDrop} target The drop target
3572                  * @param {Event} e The event object
3573                  * @param {String} id The id of the dragged element
3574                  * @method afterDragEnter
3575                  */
3576                 this.afterDragEnter(target, e, id);
3577             }
3578         }
3579     },
3580
3581     /**
3582      * An empty function by default, but provided so that you can perform a custom action
3583      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3584      * @param {Roo.dd.DragDrop} target The drop target
3585      * @param {Event} e The event object
3586      * @param {String} id The id of the dragged element
3587      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3588      */
3589     beforeDragEnter : function(target, e, id){
3590         return true;
3591     },
3592
3593     // private
3594     alignElWithMouse: function() {
3595         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3596         this.proxy.sync();
3597     },
3598
3599     // private
3600     onDragOver : function(e, id){
3601         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3602         if(this.beforeDragOver(target, e, id) !== false){
3603             if(target.isNotifyTarget){
3604                 var status = target.notifyOver(this, e, this.dragData);
3605                 this.proxy.setStatus(status);
3606             }
3607
3608             if(this.afterDragOver){
3609                 /**
3610                  * An empty function by default, but provided so that you can perform a custom action
3611                  * while the dragged item is over the drop target by providing an implementation.
3612                  * @param {Roo.dd.DragDrop} target The drop target
3613                  * @param {Event} e The event object
3614                  * @param {String} id The id of the dragged element
3615                  * @method afterDragOver
3616                  */
3617                 this.afterDragOver(target, e, id);
3618             }
3619         }
3620     },
3621
3622     /**
3623      * An empty function by default, but provided so that you can perform a custom action
3624      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3625      * @param {Roo.dd.DragDrop} target The drop target
3626      * @param {Event} e The event object
3627      * @param {String} id The id of the dragged element
3628      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3629      */
3630     beforeDragOver : function(target, e, id){
3631         return true;
3632     },
3633
3634     // private
3635     onDragOut : function(e, id){
3636         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3637         if(this.beforeDragOut(target, e, id) !== false){
3638             if(target.isNotifyTarget){
3639                 target.notifyOut(this, e, this.dragData);
3640             }
3641             this.proxy.reset();
3642             if(this.afterDragOut){
3643                 /**
3644                  * An empty function by default, but provided so that you can perform a custom action
3645                  * after the dragged item is dragged out of the target without dropping.
3646                  * @param {Roo.dd.DragDrop} target The drop target
3647                  * @param {Event} e The event object
3648                  * @param {String} id The id of the dragged element
3649                  * @method afterDragOut
3650                  */
3651                 this.afterDragOut(target, e, id);
3652             }
3653         }
3654         this.cachedTarget = null;
3655     },
3656
3657     /**
3658      * An empty function by default, but provided so that you can perform a custom action before the dragged
3659      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3660      * @param {Roo.dd.DragDrop} target The drop target
3661      * @param {Event} e The event object
3662      * @param {String} id The id of the dragged element
3663      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3664      */
3665     beforeDragOut : function(target, e, id){
3666         return true;
3667     },
3668     
3669     // private
3670     onDragDrop : function(e, id){
3671         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3672         if(this.beforeDragDrop(target, e, id) !== false){
3673             if(target.isNotifyTarget){
3674                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3675                     this.onValidDrop(target, e, id);
3676                 }else{
3677                     this.onInvalidDrop(target, e, id);
3678                 }
3679             }else{
3680                 this.onValidDrop(target, e, id);
3681             }
3682             
3683             if(this.afterDragDrop){
3684                 /**
3685                  * An empty function by default, but provided so that you can perform a custom action
3686                  * after a valid drag drop has occurred by providing an implementation.
3687                  * @param {Roo.dd.DragDrop} target The drop target
3688                  * @param {Event} e The event object
3689                  * @param {String} id The id of the dropped element
3690                  * @method afterDragDrop
3691                  */
3692                 this.afterDragDrop(target, e, id);
3693             }
3694         }
3695         delete this.cachedTarget;
3696     },
3697
3698     /**
3699      * An empty function by default, but provided so that you can perform a custom action before the dragged
3700      * item is dropped onto the target and optionally cancel the onDragDrop.
3701      * @param {Roo.dd.DragDrop} target The drop target
3702      * @param {Event} e The event object
3703      * @param {String} id The id of the dragged element
3704      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3705      */
3706     beforeDragDrop : function(target, e, id){
3707         return true;
3708     },
3709
3710     // private
3711     onValidDrop : function(target, e, id){
3712         this.hideProxy();
3713         if(this.afterValidDrop){
3714             /**
3715              * An empty function by default, but provided so that you can perform a custom action
3716              * after a valid drop has occurred by providing an implementation.
3717              * @param {Object} target The target DD 
3718              * @param {Event} e The event object
3719              * @param {String} id The id of the dropped element
3720              * @method afterInvalidDrop
3721              */
3722             this.afterValidDrop(target, e, id);
3723         }
3724     },
3725
3726     // private
3727     getRepairXY : function(e, data){
3728         return this.el.getXY();  
3729     },
3730
3731     // private
3732     onInvalidDrop : function(target, e, id){
3733         this.beforeInvalidDrop(target, e, id);
3734         if(this.cachedTarget){
3735             if(this.cachedTarget.isNotifyTarget){
3736                 this.cachedTarget.notifyOut(this, e, this.dragData);
3737             }
3738             this.cacheTarget = null;
3739         }
3740         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3741
3742         if(this.afterInvalidDrop){
3743             /**
3744              * An empty function by default, but provided so that you can perform a custom action
3745              * after an invalid drop has occurred by providing an implementation.
3746              * @param {Event} e The event object
3747              * @param {String} id The id of the dropped element
3748              * @method afterInvalidDrop
3749              */
3750             this.afterInvalidDrop(e, id);
3751         }
3752     },
3753
3754     // private
3755     afterRepair : function(){
3756         if(Roo.enableFx){
3757             this.el.highlight(this.hlColor || "c3daf9");
3758         }
3759         this.dragging = false;
3760     },
3761
3762     /**
3763      * An empty function by default, but provided so that you can perform a custom action after an invalid
3764      * drop has occurred.
3765      * @param {Roo.dd.DragDrop} target The drop target
3766      * @param {Event} e The event object
3767      * @param {String} id The id of the dragged element
3768      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3769      */
3770     beforeInvalidDrop : function(target, e, id){
3771         return true;
3772     },
3773
3774     // private
3775     handleMouseDown : function(e){
3776         if(this.dragging) {
3777             return;
3778         }
3779         var data = this.getDragData(e);
3780         if(data && this.onBeforeDrag(data, e) !== false){
3781             this.dragData = data;
3782             this.proxy.stop();
3783             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3784         } 
3785     },
3786
3787     /**
3788      * An empty function by default, but provided so that you can perform a custom action before the initial
3789      * drag event begins and optionally cancel it.
3790      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3791      * @param {Event} e The event object
3792      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3793      */
3794     onBeforeDrag : function(data, e){
3795         return true;
3796     },
3797
3798     /**
3799      * An empty function by default, but provided so that you can perform a custom action once the initial
3800      * drag event has begun.  The drag cannot be canceled from this function.
3801      * @param {Number} x The x position of the click on the dragged object
3802      * @param {Number} y The y position of the click on the dragged object
3803      */
3804     onStartDrag : Roo.emptyFn,
3805
3806     // private - YUI override
3807     startDrag : function(x, y){
3808         this.proxy.reset();
3809         this.dragging = true;
3810         this.proxy.update("");
3811         this.onInitDrag(x, y);
3812         this.proxy.show();
3813     },
3814
3815     // private
3816     onInitDrag : function(x, y){
3817         var clone = this.el.dom.cloneNode(true);
3818         clone.id = Roo.id(); // prevent duplicate ids
3819         this.proxy.update(clone);
3820         this.onStartDrag(x, y);
3821         return true;
3822     },
3823
3824     /**
3825      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3826      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3827      */
3828     getProxy : function(){
3829         return this.proxy;  
3830     },
3831
3832     /**
3833      * Hides the drag source's {@link Roo.dd.StatusProxy}
3834      */
3835     hideProxy : function(){
3836         this.proxy.hide();  
3837         this.proxy.reset(true);
3838         this.dragging = false;
3839     },
3840
3841     // private
3842     triggerCacheRefresh : function(){
3843         Roo.dd.DDM.refreshCache(this.groups);
3844     },
3845
3846     // private - override to prevent hiding
3847     b4EndDrag: function(e) {
3848     },
3849
3850     // private - override to prevent moving
3851     endDrag : function(e){
3852         this.onEndDrag(this.dragData, e);
3853     },
3854
3855     // private
3856     onEndDrag : function(data, e){
3857     },
3858     
3859     // private - pin to cursor
3860     autoOffset : function(x, y) {
3861         this.setDelta(-12, -20);
3862     }    
3863 });/*
3864  * Based on:
3865  * Ext JS Library 1.1.1
3866  * Copyright(c) 2006-2007, Ext JS, LLC.
3867  *
3868  * Originally Released Under LGPL - original licence link has changed is not relivant.
3869  *
3870  * Fork - LGPL
3871  * <script type="text/javascript">
3872  */
3873
3874
3875 /**
3876  * @class Roo.dd.DropTarget
3877  * @extends Roo.dd.DDTarget
3878  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3879  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3880  * @constructor
3881  * @param {String/HTMLElement/Element} el The container element
3882  * @param {Object} config
3883  */
3884 Roo.dd.DropTarget = function(el, config){
3885     this.el = Roo.get(el);
3886     
3887     Roo.apply(this, config);
3888     
3889     if(this.containerScroll){
3890         Roo.dd.ScrollManager.register(this.el);
3891     }
3892     
3893     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
3894           {isTarget: true});
3895
3896 };
3897
3898 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3899     /**
3900      * @cfg {String} overClass
3901      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3902      */
3903     /**
3904      * @cfg {String} dropAllowed
3905      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3906      */
3907     dropAllowed : "x-dd-drop-ok",
3908     /**
3909      * @cfg {String} dropNotAllowed
3910      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3911      */
3912     dropNotAllowed : "x-dd-drop-nodrop",
3913
3914     // private
3915     isTarget : true,
3916
3917     // private
3918     isNotifyTarget : true,
3919
3920     /**
3921      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3922      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3923      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3924      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3925      * @param {Event} e The event
3926      * @param {Object} data An object containing arbitrary data supplied by the drag source
3927      * @return {String} status The CSS class that communicates the drop status back to the source so that the
3928      * underlying {@link Roo.dd.StatusProxy} can be updated
3929      */
3930     notifyEnter : function(dd, e, data){
3931         if(this.overClass){
3932             this.el.addClass(this.overClass);
3933         }
3934         return this.dropAllowed;
3935     },
3936
3937     /**
3938      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3939      * This method will be called on every mouse movement while the drag source is over the drop target.
3940      * This default implementation simply returns the dropAllowed config value.
3941      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3942      * @param {Event} e The event
3943      * @param {Object} data An object containing arbitrary data supplied by the drag source
3944      * @return {String} status The CSS class that communicates the drop status back to the source so that the
3945      * underlying {@link Roo.dd.StatusProxy} can be updated
3946      */
3947     notifyOver : function(dd, e, data){
3948         return this.dropAllowed;
3949     },
3950
3951     /**
3952      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3953      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3954      * overClass (if any) from the drop element.
3955      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3956      * @param {Event} e The event
3957      * @param {Object} data An object containing arbitrary data supplied by the drag source
3958      */
3959     notifyOut : function(dd, e, data){
3960         if(this.overClass){
3961             this.el.removeClass(this.overClass);
3962         }
3963     },
3964
3965     /**
3966      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3967      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3968      * implementation that does something to process the drop event and returns true so that the drag source's
3969      * repair action does not run.
3970      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3971      * @param {Event} e The event
3972      * @param {Object} data An object containing arbitrary data supplied by the drag source
3973      * @return {Boolean} True if the drop was valid, else false
3974      */
3975     notifyDrop : function(dd, e, data){
3976         return false;
3977     }
3978 });/*
3979  * Based on:
3980  * Ext JS Library 1.1.1
3981  * Copyright(c) 2006-2007, Ext JS, LLC.
3982  *
3983  * Originally Released Under LGPL - original licence link has changed is not relivant.
3984  *
3985  * Fork - LGPL
3986  * <script type="text/javascript">
3987  */
3988
3989
3990 /**
3991  * @class Roo.dd.DragZone
3992  * @extends Roo.dd.DragSource
3993  * This class provides a container DD instance that proxies for multiple child node sources.<br />
3994  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
3995  * @constructor
3996  * @param {String/HTMLElement/Element} el The container element
3997  * @param {Object} config
3998  */
3999 Roo.dd.DragZone = function(el, config){
4000     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4001     if(this.containerScroll){
4002         Roo.dd.ScrollManager.register(this.el);
4003     }
4004 };
4005
4006 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4007     /**
4008      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4009      * for auto scrolling during drag operations.
4010      */
4011     /**
4012      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4013      * method after a failed drop (defaults to "c3daf9" - light blue)
4014      */
4015
4016     /**
4017      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4018      * for a valid target to drag based on the mouse down. Override this method
4019      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4020      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4021      * @param {EventObject} e The mouse down event
4022      * @return {Object} The dragData
4023      */
4024     getDragData : function(e){
4025         return Roo.dd.Registry.getHandleFromEvent(e);
4026     },
4027     
4028     /**
4029      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4030      * this.dragData.ddel
4031      * @param {Number} x The x position of the click on the dragged object
4032      * @param {Number} y The y position of the click on the dragged object
4033      * @return {Boolean} true to continue the drag, false to cancel
4034      */
4035     onInitDrag : function(x, y){
4036         this.proxy.update(this.dragData.ddel.cloneNode(true));
4037         this.onStartDrag(x, y);
4038         return true;
4039     },
4040     
4041     /**
4042      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4043      */
4044     afterRepair : function(){
4045         if(Roo.enableFx){
4046             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4047         }
4048         this.dragging = false;
4049     },
4050
4051     /**
4052      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4053      * the XY of this.dragData.ddel
4054      * @param {EventObject} e The mouse up event
4055      * @return {Array} The xy location (e.g. [100, 200])
4056      */
4057     getRepairXY : function(e){
4058         return Roo.Element.fly(this.dragData.ddel).getXY();  
4059     }
4060 });/*
4061  * Based on:
4062  * Ext JS Library 1.1.1
4063  * Copyright(c) 2006-2007, Ext JS, LLC.
4064  *
4065  * Originally Released Under LGPL - original licence link has changed is not relivant.
4066  *
4067  * Fork - LGPL
4068  * <script type="text/javascript">
4069  */
4070 /**
4071  * @class Roo.dd.DropZone
4072  * @extends Roo.dd.DropTarget
4073  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4074  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4075  * @constructor
4076  * @param {String/HTMLElement/Element} el The container element
4077  * @param {Object} config
4078  */
4079 Roo.dd.DropZone = function(el, config){
4080     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4081 };
4082
4083 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4084     /**
4085      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4086      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4087      * provide your own custom lookup.
4088      * @param {Event} e The event
4089      * @return {Object} data The custom data
4090      */
4091     getTargetFromEvent : function(e){
4092         return Roo.dd.Registry.getTargetFromEvent(e);
4093     },
4094
4095     /**
4096      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4097      * that it has registered.  This method has no default implementation and should be overridden to provide
4098      * node-specific processing if necessary.
4099      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4100      * {@link #getTargetFromEvent} for this node)
4101      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4102      * @param {Event} e The event
4103      * @param {Object} data An object containing arbitrary data supplied by the drag source
4104      */
4105     onNodeEnter : function(n, dd, e, data){
4106         
4107     },
4108
4109     /**
4110      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4111      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4112      * overridden to provide the proper feedback.
4113      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4114      * {@link #getTargetFromEvent} for this node)
4115      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4116      * @param {Event} e The event
4117      * @param {Object} data An object containing arbitrary data supplied by the drag source
4118      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4119      * underlying {@link Roo.dd.StatusProxy} can be updated
4120      */
4121     onNodeOver : function(n, dd, e, data){
4122         return this.dropAllowed;
4123     },
4124
4125     /**
4126      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4127      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4128      * node-specific processing if necessary.
4129      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4130      * {@link #getTargetFromEvent} for this node)
4131      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4132      * @param {Event} e The event
4133      * @param {Object} data An object containing arbitrary data supplied by the drag source
4134      */
4135     onNodeOut : function(n, dd, e, data){
4136         
4137     },
4138
4139     /**
4140      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4141      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4142      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4143      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4144      * {@link #getTargetFromEvent} for this node)
4145      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4146      * @param {Event} e The event
4147      * @param {Object} data An object containing arbitrary data supplied by the drag source
4148      * @return {Boolean} True if the drop was valid, else false
4149      */
4150     onNodeDrop : function(n, dd, e, data){
4151         return false;
4152     },
4153
4154     /**
4155      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4156      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4157      * it should be overridden to provide the proper feedback if necessary.
4158      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4159      * @param {Event} e The event
4160      * @param {Object} data An object containing arbitrary data supplied by the drag source
4161      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4162      * underlying {@link Roo.dd.StatusProxy} can be updated
4163      */
4164     onContainerOver : function(dd, e, data){
4165         return this.dropNotAllowed;
4166     },
4167
4168     /**
4169      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4170      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4171      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4172      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4173      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4174      * @param {Event} e The event
4175      * @param {Object} data An object containing arbitrary data supplied by the drag source
4176      * @return {Boolean} True if the drop was valid, else false
4177      */
4178     onContainerDrop : function(dd, e, data){
4179         return false;
4180     },
4181
4182     /**
4183      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4184      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4185      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4186      * you should override this method and provide a custom implementation.
4187      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4188      * @param {Event} e The event
4189      * @param {Object} data An object containing arbitrary data supplied by the drag source
4190      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4191      * underlying {@link Roo.dd.StatusProxy} can be updated
4192      */
4193     notifyEnter : function(dd, e, data){
4194         return this.dropNotAllowed;
4195     },
4196
4197     /**
4198      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4199      * This method will be called on every mouse movement while the drag source is over the drop zone.
4200      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4201      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4202      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4203      * registered node, it will call {@link #onContainerOver}.
4204      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4205      * @param {Event} e The event
4206      * @param {Object} data An object containing arbitrary data supplied by the drag source
4207      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4208      * underlying {@link Roo.dd.StatusProxy} can be updated
4209      */
4210     notifyOver : function(dd, e, data){
4211         var n = this.getTargetFromEvent(e);
4212         if(!n){ // not over valid drop target
4213             if(this.lastOverNode){
4214                 this.onNodeOut(this.lastOverNode, dd, e, data);
4215                 this.lastOverNode = null;
4216             }
4217             return this.onContainerOver(dd, e, data);
4218         }
4219         if(this.lastOverNode != n){
4220             if(this.lastOverNode){
4221                 this.onNodeOut(this.lastOverNode, dd, e, data);
4222             }
4223             this.onNodeEnter(n, dd, e, data);
4224             this.lastOverNode = n;
4225         }
4226         return this.onNodeOver(n, dd, e, data);
4227     },
4228
4229     /**
4230      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4231      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4232      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4233      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4234      * @param {Event} e The event
4235      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4236      */
4237     notifyOut : function(dd, e, data){
4238         if(this.lastOverNode){
4239             this.onNodeOut(this.lastOverNode, dd, e, data);
4240             this.lastOverNode = null;
4241         }
4242     },
4243
4244     /**
4245      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4246      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4247      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4248      * otherwise it will call {@link #onContainerDrop}.
4249      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4250      * @param {Event} e The event
4251      * @param {Object} data An object containing arbitrary data supplied by the drag source
4252      * @return {Boolean} True if the drop was valid, else false
4253      */
4254     notifyDrop : function(dd, e, data){
4255         if(this.lastOverNode){
4256             this.onNodeOut(this.lastOverNode, dd, e, data);
4257             this.lastOverNode = null;
4258         }
4259         var n = this.getTargetFromEvent(e);
4260         return n ?
4261             this.onNodeDrop(n, dd, e, data) :
4262             this.onContainerDrop(dd, e, data);
4263     },
4264
4265     // private
4266     triggerCacheRefresh : function(){
4267         Roo.dd.DDM.refreshCache(this.groups);
4268     }  
4269 });/*
4270  * Based on:
4271  * Ext JS Library 1.1.1
4272  * Copyright(c) 2006-2007, Ext JS, LLC.
4273  *
4274  * Originally Released Under LGPL - original licence link has changed is not relivant.
4275  *
4276  * Fork - LGPL
4277  * <script type="text/javascript">
4278  */
4279
4280
4281 /**
4282  * @class Roo.data.SortTypes
4283  * @singleton
4284  * Defines the default sorting (casting?) comparison functions used when sorting data.
4285  */
4286 Roo.data.SortTypes = {
4287     /**
4288      * Default sort that does nothing
4289      * @param {Mixed} s The value being converted
4290      * @return {Mixed} The comparison value
4291      */
4292     none : function(s){
4293         return s;
4294     },
4295     
4296     /**
4297      * The regular expression used to strip tags
4298      * @type {RegExp}
4299      * @property
4300      */
4301     stripTagsRE : /<\/?[^>]+>/gi,
4302     
4303     /**
4304      * Strips all HTML tags to sort on text only
4305      * @param {Mixed} s The value being converted
4306      * @return {String} The comparison value
4307      */
4308     asText : function(s){
4309         return String(s).replace(this.stripTagsRE, "");
4310     },
4311     
4312     /**
4313      * Strips all HTML tags to sort on text only - Case insensitive
4314      * @param {Mixed} s The value being converted
4315      * @return {String} The comparison value
4316      */
4317     asUCText : function(s){
4318         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4319     },
4320     
4321     /**
4322      * Case insensitive string
4323      * @param {Mixed} s The value being converted
4324      * @return {String} The comparison value
4325      */
4326     asUCString : function(s) {
4327         return String(s).toUpperCase();
4328     },
4329     
4330     /**
4331      * Date sorting
4332      * @param {Mixed} s The value being converted
4333      * @return {Number} The comparison value
4334      */
4335     asDate : function(s) {
4336         if(!s){
4337             return 0;
4338         }
4339         if(s instanceof Date){
4340             return s.getTime();
4341         }
4342         return Date.parse(String(s));
4343     },
4344     
4345     /**
4346      * Float sorting
4347      * @param {Mixed} s The value being converted
4348      * @return {Float} The comparison value
4349      */
4350     asFloat : function(s) {
4351         var val = parseFloat(String(s).replace(/,/g, ""));
4352         if(isNaN(val)) val = 0;
4353         return val;
4354     },
4355     
4356     /**
4357      * Integer sorting
4358      * @param {Mixed} s The value being converted
4359      * @return {Number} The comparison value
4360      */
4361     asInt : function(s) {
4362         var val = parseInt(String(s).replace(/,/g, ""));
4363         if(isNaN(val)) val = 0;
4364         return val;
4365     }
4366 };/*
4367  * Based on:
4368  * Ext JS Library 1.1.1
4369  * Copyright(c) 2006-2007, Ext JS, LLC.
4370  *
4371  * Originally Released Under LGPL - original licence link has changed is not relivant.
4372  *
4373  * Fork - LGPL
4374  * <script type="text/javascript">
4375  */
4376
4377 /**
4378 * @class Roo.data.Record
4379  * Instances of this class encapsulate both record <em>definition</em> information, and record
4380  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4381  * to access Records cached in an {@link Roo.data.Store} object.<br>
4382  * <p>
4383  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4384  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4385  * objects.<br>
4386  * <p>
4387  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4388  * @constructor
4389  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4390  * {@link #create}. The parameters are the same.
4391  * @param {Array} data An associative Array of data values keyed by the field name.
4392  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4393  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4394  * not specified an integer id is generated.
4395  */
4396 Roo.data.Record = function(data, id){
4397     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4398     this.data = data;
4399 };
4400
4401 /**
4402  * Generate a constructor for a specific record layout.
4403  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4404  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4405  * Each field definition object may contain the following properties: <ul>
4406  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4407  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4408  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4409  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4410  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4411  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4412  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4413  * this may be omitted.</p></li>
4414  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4415  * <ul><li>auto (Default, implies no conversion)</li>
4416  * <li>string</li>
4417  * <li>int</li>
4418  * <li>float</li>
4419  * <li>boolean</li>
4420  * <li>date</li></ul></p></li>
4421  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4422  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4423  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4424  * by the Reader into an object that will be stored in the Record. It is passed the
4425  * following parameters:<ul>
4426  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4427  * </ul></p></li>
4428  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4429  * </ul>
4430  * <br>usage:<br><pre><code>
4431 var TopicRecord = Roo.data.Record.create(
4432     {name: 'title', mapping: 'topic_title'},
4433     {name: 'author', mapping: 'username'},
4434     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4435     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4436     {name: 'lastPoster', mapping: 'user2'},
4437     {name: 'excerpt', mapping: 'post_text'}
4438 );
4439
4440 var myNewRecord = new TopicRecord({
4441     title: 'Do my job please',
4442     author: 'noobie',
4443     totalPosts: 1,
4444     lastPost: new Date(),
4445     lastPoster: 'Animal',
4446     excerpt: 'No way dude!'
4447 });
4448 myStore.add(myNewRecord);
4449 </code></pre>
4450  * @method create
4451  * @static
4452  */
4453 Roo.data.Record.create = function(o){
4454     var f = function(){
4455         f.superclass.constructor.apply(this, arguments);
4456     };
4457     Roo.extend(f, Roo.data.Record);
4458     var p = f.prototype;
4459     p.fields = new Roo.util.MixedCollection(false, function(field){
4460         return field.name;
4461     });
4462     for(var i = 0, len = o.length; i < len; i++){
4463         p.fields.add(new Roo.data.Field(o[i]));
4464     }
4465     f.getField = function(name){
4466         return p.fields.get(name);  
4467     };
4468     return f;
4469 };
4470
4471 Roo.data.Record.AUTO_ID = 1000;
4472 Roo.data.Record.EDIT = 'edit';
4473 Roo.data.Record.REJECT = 'reject';
4474 Roo.data.Record.COMMIT = 'commit';
4475
4476 Roo.data.Record.prototype = {
4477     /**
4478      * Readonly flag - true if this record has been modified.
4479      * @type Boolean
4480      */
4481     dirty : false,
4482     editing : false,
4483     error: null,
4484     modified: null,
4485
4486     // private
4487     join : function(store){
4488         this.store = store;
4489     },
4490
4491     /**
4492      * Set the named field to the specified value.
4493      * @param {String} name The name of the field to set.
4494      * @param {Object} value The value to set the field to.
4495      */
4496     set : function(name, value){
4497         if(this.data[name] == value){
4498             return;
4499         }
4500         this.dirty = true;
4501         if(!this.modified){
4502             this.modified = {};
4503         }
4504         if(typeof this.modified[name] == 'undefined'){
4505             this.modified[name] = this.data[name];
4506         }
4507         this.data[name] = value;
4508         if(!this.editing){
4509             this.store.afterEdit(this);
4510         }       
4511     },
4512
4513     /**
4514      * Get the value of the named field.
4515      * @param {String} name The name of the field to get the value of.
4516      * @return {Object} The value of the field.
4517      */
4518     get : function(name){
4519         return this.data[name]; 
4520     },
4521
4522     // private
4523     beginEdit : function(){
4524         this.editing = true;
4525         this.modified = {}; 
4526     },
4527
4528     // private
4529     cancelEdit : function(){
4530         this.editing = false;
4531         delete this.modified;
4532     },
4533
4534     // private
4535     endEdit : function(){
4536         this.editing = false;
4537         if(this.dirty && this.store){
4538             this.store.afterEdit(this);
4539         }
4540     },
4541
4542     /**
4543      * Usually called by the {@link Roo.data.Store} which owns the Record.
4544      * Rejects all changes made to the Record since either creation, or the last commit operation.
4545      * Modified fields are reverted to their original values.
4546      * <p>
4547      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4548      * of reject operations.
4549      */
4550     reject : function(){
4551         var m = this.modified;
4552         for(var n in m){
4553             if(typeof m[n] != "function"){
4554                 this.data[n] = m[n];
4555             }
4556         }
4557         this.dirty = false;
4558         delete this.modified;
4559         this.editing = false;
4560         if(this.store){
4561             this.store.afterReject(this);
4562         }
4563     },
4564
4565     /**
4566      * Usually called by the {@link Roo.data.Store} which owns the Record.
4567      * Commits all changes made to the Record since either creation, or the last commit operation.
4568      * <p>
4569      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4570      * of commit operations.
4571      */
4572     commit : function(){
4573         this.dirty = false;
4574         delete this.modified;
4575         this.editing = false;
4576         if(this.store){
4577             this.store.afterCommit(this);
4578         }
4579     },
4580
4581     // private
4582     hasError : function(){
4583         return this.error != null;
4584     },
4585
4586     // private
4587     clearError : function(){
4588         this.error = null;
4589     },
4590
4591     /**
4592      * Creates a copy of this record.
4593      * @param {String} id (optional) A new record id if you don't want to use this record's id
4594      * @return {Record}
4595      */
4596     copy : function(newId) {
4597         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4598     }
4599 };/*
4600  * Based on:
4601  * Ext JS Library 1.1.1
4602  * Copyright(c) 2006-2007, Ext JS, LLC.
4603  *
4604  * Originally Released Under LGPL - original licence link has changed is not relivant.
4605  *
4606  * Fork - LGPL
4607  * <script type="text/javascript">
4608  */
4609
4610
4611
4612 /**
4613  * @class Roo.data.Store
4614  * @extends Roo.util.Observable
4615  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4616  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4617  * <p>
4618  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4619  * has no knowledge of the format of the data returned by the Proxy.<br>
4620  * <p>
4621  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4622  * instances from the data object. These records are cached and made available through accessor functions.
4623  * @constructor
4624  * Creates a new Store.
4625  * @param {Object} config A config object containing the objects needed for the Store to access data,
4626  * and read the data into Records.
4627  */
4628 Roo.data.Store = function(config){
4629     this.data = new Roo.util.MixedCollection(false);
4630     this.data.getKey = function(o){
4631         return o.id;
4632     };
4633     this.baseParams = {};
4634     // private
4635     this.paramNames = {
4636         "start" : "start",
4637         "limit" : "limit",
4638         "sort" : "sort",
4639         "dir" : "dir"
4640     };
4641
4642     if(config && config.data){
4643         this.inlineData = config.data;
4644         delete config.data;
4645     }
4646
4647     Roo.apply(this, config);
4648     
4649     if(this.reader){ // reader passed
4650         this.reader = Roo.factory(this.reader, Roo.data);
4651         this.reader.xmodule = this.xmodule || false;
4652         if(!this.recordType){
4653             this.recordType = this.reader.recordType;
4654         }
4655         if(this.reader.onMetaChange){
4656             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4657         }
4658     }
4659
4660     if(this.recordType){
4661         this.fields = this.recordType.prototype.fields;
4662     }
4663     this.modified = [];
4664
4665     this.addEvents({
4666         /**
4667          * @event datachanged
4668          * Fires when the data cache has changed, and a widget which is using this Store
4669          * as a Record cache should refresh its view.
4670          * @param {Store} this
4671          */
4672         datachanged : true,
4673         /**
4674          * @event metachange
4675          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4676          * @param {Store} this
4677          * @param {Object} meta The JSON metadata
4678          */
4679         metachange : true,
4680         /**
4681          * @event add
4682          * Fires when Records have been added to the Store
4683          * @param {Store} this
4684          * @param {Roo.data.Record[]} records The array of Records added
4685          * @param {Number} index The index at which the record(s) were added
4686          */
4687         add : true,
4688         /**
4689          * @event remove
4690          * Fires when a Record has been removed from the Store
4691          * @param {Store} this
4692          * @param {Roo.data.Record} record The Record that was removed
4693          * @param {Number} index The index at which the record was removed
4694          */
4695         remove : true,
4696         /**
4697          * @event update
4698          * Fires when a Record has been updated
4699          * @param {Store} this
4700          * @param {Roo.data.Record} record The Record that was updated
4701          * @param {String} operation The update operation being performed.  Value may be one of:
4702          * <pre><code>
4703  Roo.data.Record.EDIT
4704  Roo.data.Record.REJECT
4705  Roo.data.Record.COMMIT
4706          * </code></pre>
4707          */
4708         update : true,
4709         /**
4710          * @event clear
4711          * Fires when the data cache has been cleared.
4712          * @param {Store} this
4713          */
4714         clear : true,
4715         /**
4716          * @event beforeload
4717          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4718          * the load action will be canceled.
4719          * @param {Store} this
4720          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4721          */
4722         beforeload : true,
4723         /**
4724          * @event load
4725          * Fires after a new set of Records has been loaded.
4726          * @param {Store} this
4727          * @param {Roo.data.Record[]} records The Records that were loaded
4728          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4729          */
4730         load : true,
4731         /**
4732          * @event loadexception
4733          * Fires if an exception occurs in the Proxy during loading.
4734          * Called with the signature of the Proxy's "loadexception" event.
4735          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4736          * 
4737          * @param {Proxy} 
4738          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4739          * @param {Object} load options 
4740          * @param {Object} jsonData from your request (normally this contains the Exception)
4741          */
4742         loadexception : true
4743     });
4744     
4745     if(this.proxy){
4746         this.proxy = Roo.factory(this.proxy, Roo.data);
4747         this.proxy.xmodule = this.xmodule || false;
4748         this.relayEvents(this.proxy,  ["loadexception"]);
4749     }
4750     this.sortToggle = {};
4751
4752     Roo.data.Store.superclass.constructor.call(this);
4753
4754     if(this.inlineData){
4755         this.loadData(this.inlineData);
4756         delete this.inlineData;
4757     }
4758 };
4759 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4760      /**
4761     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4762     * without a remote query - used by combo/forms at present.
4763     */
4764     
4765     /**
4766     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4767     */
4768     /**
4769     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4770     */
4771     /**
4772     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4773     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4774     */
4775     /**
4776     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4777     * on any HTTP request
4778     */
4779     /**
4780     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4781     */
4782     /**
4783     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4784     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4785     */
4786     remoteSort : false,
4787
4788     /**
4789     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4790      * loaded or when a record is removed. (defaults to false).
4791     */
4792     pruneModifiedRecords : false,
4793
4794     // private
4795     lastOptions : null,
4796
4797     /**
4798      * Add Records to the Store and fires the add event.
4799      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4800      */
4801     add : function(records){
4802         records = [].concat(records);
4803         for(var i = 0, len = records.length; i < len; i++){
4804             records[i].join(this);
4805         }
4806         var index = this.data.length;
4807         this.data.addAll(records);
4808         this.fireEvent("add", this, records, index);
4809     },
4810
4811     /**
4812      * Remove a Record from the Store and fires the remove event.
4813      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4814      */
4815     remove : function(record){
4816         var index = this.data.indexOf(record);
4817         this.data.removeAt(index);
4818         if(this.pruneModifiedRecords){
4819             this.modified.remove(record);
4820         }
4821         this.fireEvent("remove", this, record, index);
4822     },
4823
4824     /**
4825      * Remove all Records from the Store and fires the clear event.
4826      */
4827     removeAll : function(){
4828         this.data.clear();
4829         if(this.pruneModifiedRecords){
4830             this.modified = [];
4831         }
4832         this.fireEvent("clear", this);
4833     },
4834
4835     /**
4836      * Inserts Records to the Store at the given index and fires the add event.
4837      * @param {Number} index The start index at which to insert the passed Records.
4838      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4839      */
4840     insert : function(index, records){
4841         records = [].concat(records);
4842         for(var i = 0, len = records.length; i < len; i++){
4843             this.data.insert(index, records[i]);
4844             records[i].join(this);
4845         }
4846         this.fireEvent("add", this, records, index);
4847     },
4848
4849     /**
4850      * Get the index within the cache of the passed Record.
4851      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4852      * @return {Number} The index of the passed Record. Returns -1 if not found.
4853      */
4854     indexOf : function(record){
4855         return this.data.indexOf(record);
4856     },
4857
4858     /**
4859      * Get the index within the cache of the Record with the passed id.
4860      * @param {String} id The id of the Record to find.
4861      * @return {Number} The index of the Record. Returns -1 if not found.
4862      */
4863     indexOfId : function(id){
4864         return this.data.indexOfKey(id);
4865     },
4866
4867     /**
4868      * Get the Record with the specified id.
4869      * @param {String} id The id of the Record to find.
4870      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4871      */
4872     getById : function(id){
4873         return this.data.key(id);
4874     },
4875
4876     /**
4877      * Get the Record at the specified index.
4878      * @param {Number} index The index of the Record to find.
4879      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4880      */
4881     getAt : function(index){
4882         return this.data.itemAt(index);
4883     },
4884
4885     /**
4886      * Returns a range of Records between specified indices.
4887      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4888      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4889      * @return {Roo.data.Record[]} An array of Records
4890      */
4891     getRange : function(start, end){
4892         return this.data.getRange(start, end);
4893     },
4894
4895     // private
4896     storeOptions : function(o){
4897         o = Roo.apply({}, o);
4898         delete o.callback;
4899         delete o.scope;
4900         this.lastOptions = o;
4901     },
4902
4903     /**
4904      * Loads the Record cache from the configured Proxy using the configured Reader.
4905      * <p>
4906      * If using remote paging, then the first load call must specify the <em>start</em>
4907      * and <em>limit</em> properties in the options.params property to establish the initial
4908      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4909      * <p>
4910      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4911      * and this call will return before the new data has been loaded. Perform any post-processing
4912      * in a callback function, or in a "load" event handler.</strong>
4913      * <p>
4914      * @param {Object} options An object containing properties which control loading options:<ul>
4915      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4916      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4917      * passed the following arguments:<ul>
4918      * <li>r : Roo.data.Record[]</li>
4919      * <li>options: Options object from the load call</li>
4920      * <li>success: Boolean success indicator</li></ul></li>
4921      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4922      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4923      * </ul>
4924      */
4925     load : function(options){
4926         options = options || {};
4927         if(this.fireEvent("beforeload", this, options) !== false){
4928             this.storeOptions(options);
4929             var p = Roo.apply(options.params || {}, this.baseParams);
4930             if(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("my-element",
8979  '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8980  {
8981  singleSelect: true,
8982  selectedClass: "ydataview-selected",
8983  store: store
8984  });
8985
8986  // listen for node click?
8987  view.on("click", function(vw, index, node, e){
8988  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8989  });
8990
8991  // load XML data
8992  dataModel.load("foobar.xml");
8993  </code></pre>
8994  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8995  * <br><br>
8996  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8997  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8998  * @constructor
8999  * Create a new View
9000  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
9001  * @param {String/DomHelper.Template} tpl The rendering template or a string to create a template with
9002  * @param {Object} config The config object
9003  */
9004 Roo.View = function(container, tpl, config){
9005     this.el = Roo.get(container);
9006     if(typeof tpl == "string"){
9007         tpl = new Roo.Template(tpl);
9008     }
9009     tpl.compile();
9010     /**
9011      * The template used by this View
9012      * @type {Roo.DomHelper.Template}
9013      */
9014     this.tpl = tpl;
9015
9016     Roo.apply(this, config);
9017
9018     /** @private */
9019     this.addEvents({
9020     /**
9021      * @event beforeclick
9022      * Fires before a click is processed. Returns false to cancel the default action.
9023      * @param {Roo.View} this
9024      * @param {Number} index The index of the target node
9025      * @param {HTMLElement} node The target node
9026      * @param {Roo.EventObject} e The raw event object
9027      */
9028         "beforeclick" : true,
9029     /**
9030      * @event click
9031      * Fires when a template node is clicked.
9032      * @param {Roo.View} this
9033      * @param {Number} index The index of the target node
9034      * @param {HTMLElement} node The target node
9035      * @param {Roo.EventObject} e The raw event object
9036      */
9037         "click" : true,
9038     /**
9039      * @event dblclick
9040      * Fires when a template node is double clicked.
9041      * @param {Roo.View} this
9042      * @param {Number} index The index of the target node
9043      * @param {HTMLElement} node The target node
9044      * @param {Roo.EventObject} e The raw event object
9045      */
9046         "dblclick" : true,
9047     /**
9048      * @event contextmenu
9049      * Fires when a template node is right clicked.
9050      * @param {Roo.View} this
9051      * @param {Number} index The index of the target node
9052      * @param {HTMLElement} node The target node
9053      * @param {Roo.EventObject} e The raw event object
9054      */
9055         "contextmenu" : true,
9056     /**
9057      * @event selectionchange
9058      * Fires when the selected nodes change.
9059      * @param {Roo.View} this
9060      * @param {Array} selections Array of the selected nodes
9061      */
9062         "selectionchange" : true,
9063
9064     /**
9065      * @event beforeselect
9066      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9067      * @param {Roo.View} this
9068      * @param {HTMLElement} node The node to be selected
9069      * @param {Array} selections Array of currently selected nodes
9070      */
9071         "beforeselect" : true
9072     });
9073
9074     this.el.on({
9075         "click": this.onClick,
9076         "dblclick": this.onDblClick,
9077         "contextmenu": this.onContextMenu,
9078         scope:this
9079     });
9080
9081     this.selections = [];
9082     this.nodes = [];
9083     this.cmp = new Roo.CompositeElementLite([]);
9084     if(this.store){
9085         this.store = Roo.factory(this.store, Roo.data);
9086         this.setStore(this.store, true);
9087     }
9088     Roo.View.superclass.constructor.call(this);
9089 };
9090
9091 Roo.extend(Roo.View, Roo.util.Observable, {
9092     /**
9093      * The css class to add to selected nodes
9094      * @type {Roo.DomHelper.Template}
9095      */
9096     selectedClass : "x-view-selected",
9097     
9098     emptyText : "",
9099     /**
9100      * Returns the element this view is bound to.
9101      * @return {Roo.Element}
9102      */
9103     getEl : function(){
9104         return this.el;
9105     },
9106
9107     /**
9108      * Refreshes the view.
9109      */
9110     refresh : function(){
9111         var t = this.tpl;
9112         this.clearSelections();
9113         this.el.update("");
9114         var html = [];
9115         var records = this.store.getRange();
9116         if(records.length < 1){
9117             this.el.update(this.emptyText);
9118             return;
9119         }
9120         for(var i = 0, len = records.length; i < len; i++){
9121             var data = this.prepareData(records[i].data, i, records[i]);
9122             html[html.length] = t.apply(data);
9123         }
9124         this.el.update(html.join(""));
9125         this.nodes = this.el.dom.childNodes;
9126         this.updateIndexes(0);
9127     },
9128
9129     /**
9130      * Function to override to reformat the data that is sent to
9131      * the template for each node.
9132      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9133      * a JSON object for an UpdateManager bound view).
9134      */
9135     prepareData : function(data){
9136         return data;
9137     },
9138
9139     onUpdate : function(ds, record){
9140         this.clearSelections();
9141         var index = this.store.indexOf(record);
9142         var n = this.nodes[index];
9143         this.tpl.insertBefore(n, this.prepareData(record.data));
9144         n.parentNode.removeChild(n);
9145         this.updateIndexes(index, index);
9146     },
9147
9148     onAdd : function(ds, records, index){
9149         this.clearSelections();
9150         if(this.nodes.length == 0){
9151             this.refresh();
9152             return;
9153         }
9154         var n = this.nodes[index];
9155         for(var i = 0, len = records.length; i < len; i++){
9156             var d = this.prepareData(records[i].data);
9157             if(n){
9158                 this.tpl.insertBefore(n, d);
9159             }else{
9160                 this.tpl.append(this.el, d);
9161             }
9162         }
9163         this.updateIndexes(index);
9164     },
9165
9166     onRemove : function(ds, record, index){
9167         this.clearSelections();
9168         this.el.dom.removeChild(this.nodes[index]);
9169         this.updateIndexes(index);
9170     },
9171
9172     /**
9173      * Refresh an individual node.
9174      * @param {Number} index
9175      */
9176     refreshNode : function(index){
9177         this.onUpdate(this.store, this.store.getAt(index));
9178     },
9179
9180     updateIndexes : function(startIndex, endIndex){
9181         var ns = this.nodes;
9182         startIndex = startIndex || 0;
9183         endIndex = endIndex || ns.length - 1;
9184         for(var i = startIndex; i <= endIndex; i++){
9185             ns[i].nodeIndex = i;
9186         }
9187     },
9188
9189     /**
9190      * Changes the data store this view uses and refresh the view.
9191      * @param {Store} store
9192      */
9193     setStore : function(store, initial){
9194         if(!initial && this.store){
9195             this.store.un("datachanged", this.refresh);
9196             this.store.un("add", this.onAdd);
9197             this.store.un("remove", this.onRemove);
9198             this.store.un("update", this.onUpdate);
9199             this.store.un("clear", this.refresh);
9200         }
9201         if(store){
9202           
9203             store.on("datachanged", this.refresh, this);
9204             store.on("add", this.onAdd, this);
9205             store.on("remove", this.onRemove, this);
9206             store.on("update", this.onUpdate, this);
9207             store.on("clear", this.refresh, this);
9208         }
9209         
9210         if(store){
9211             this.refresh();
9212         }
9213     },
9214
9215     /**
9216      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9217      * @param {HTMLElement} node
9218      * @return {HTMLElement} The template node
9219      */
9220     findItemFromChild : function(node){
9221         var el = this.el.dom;
9222         if(!node || node.parentNode == el){
9223                     return node;
9224             }
9225             var p = node.parentNode;
9226             while(p && p != el){
9227             if(p.parentNode == el){
9228                 return p;
9229             }
9230             p = p.parentNode;
9231         }
9232             return null;
9233     },
9234
9235     /** @ignore */
9236     onClick : function(e){
9237         var item = this.findItemFromChild(e.getTarget());
9238         if(item){
9239             var index = this.indexOf(item);
9240             if(this.onItemClick(item, index, e) !== false){
9241                 this.fireEvent("click", this, index, item, e);
9242             }
9243         }else{
9244             this.clearSelections();
9245         }
9246     },
9247
9248     /** @ignore */
9249     onContextMenu : function(e){
9250         var item = this.findItemFromChild(e.getTarget());
9251         if(item){
9252             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9253         }
9254     },
9255
9256     /** @ignore */
9257     onDblClick : function(e){
9258         var item = this.findItemFromChild(e.getTarget());
9259         if(item){
9260             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9261         }
9262     },
9263
9264     onItemClick : function(item, index, e){
9265         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9266             return false;
9267         }
9268         if(this.multiSelect || this.singleSelect){
9269             if(this.multiSelect && e.shiftKey && this.lastSelection){
9270                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9271             }else{
9272                 this.select(item, this.multiSelect && e.ctrlKey);
9273                 this.lastSelection = item;
9274             }
9275             e.preventDefault();
9276         }
9277         return true;
9278     },
9279
9280     /**
9281      * Get the number of selected nodes.
9282      * @return {Number}
9283      */
9284     getSelectionCount : function(){
9285         return this.selections.length;
9286     },
9287
9288     /**
9289      * Get the currently selected nodes.
9290      * @return {Array} An array of HTMLElements
9291      */
9292     getSelectedNodes : function(){
9293         return this.selections;
9294     },
9295
9296     /**
9297      * Get the indexes of the selected nodes.
9298      * @return {Array}
9299      */
9300     getSelectedIndexes : function(){
9301         var indexes = [], s = this.selections;
9302         for(var i = 0, len = s.length; i < len; i++){
9303             indexes.push(s[i].nodeIndex);
9304         }
9305         return indexes;
9306     },
9307
9308     /**
9309      * Clear all selections
9310      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9311      */
9312     clearSelections : function(suppressEvent){
9313         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9314             this.cmp.elements = this.selections;
9315             this.cmp.removeClass(this.selectedClass);
9316             this.selections = [];
9317             if(!suppressEvent){
9318                 this.fireEvent("selectionchange", this, this.selections);
9319             }
9320         }
9321     },
9322
9323     /**
9324      * Returns true if the passed node is selected
9325      * @param {HTMLElement/Number} node The node or node index
9326      * @return {Boolean}
9327      */
9328     isSelected : function(node){
9329         var s = this.selections;
9330         if(s.length < 1){
9331             return false;
9332         }
9333         node = this.getNode(node);
9334         return s.indexOf(node) !== -1;
9335     },
9336
9337     /**
9338      * Selects nodes.
9339      * @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
9340      * @param {Boolean} keepExisting (optional) true to keep existing selections
9341      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9342      */
9343     select : function(nodeInfo, keepExisting, suppressEvent){
9344         if(nodeInfo instanceof Array){
9345             if(!keepExisting){
9346                 this.clearSelections(true);
9347             }
9348             for(var i = 0, len = nodeInfo.length; i < len; i++){
9349                 this.select(nodeInfo[i], true, true);
9350             }
9351         } else{
9352             var node = this.getNode(nodeInfo);
9353             if(node && !this.isSelected(node)){
9354                 if(!keepExisting){
9355                     this.clearSelections(true);
9356                 }
9357                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9358                     Roo.fly(node).addClass(this.selectedClass);
9359                     this.selections.push(node);
9360                     if(!suppressEvent){
9361                         this.fireEvent("selectionchange", this, this.selections);
9362                     }
9363                 }
9364             }
9365         }
9366     },
9367
9368     /**
9369      * Gets a template node.
9370      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9371      * @return {HTMLElement} The node or null if it wasn't found
9372      */
9373     getNode : function(nodeInfo){
9374         if(typeof nodeInfo == "string"){
9375             return document.getElementById(nodeInfo);
9376         }else if(typeof nodeInfo == "number"){
9377             return this.nodes[nodeInfo];
9378         }
9379         return nodeInfo;
9380     },
9381
9382     /**
9383      * Gets a range template nodes.
9384      * @param {Number} startIndex
9385      * @param {Number} endIndex
9386      * @return {Array} An array of nodes
9387      */
9388     getNodes : function(start, end){
9389         var ns = this.nodes;
9390         start = start || 0;
9391         end = typeof end == "undefined" ? ns.length - 1 : end;
9392         var nodes = [];
9393         if(start <= end){
9394             for(var i = start; i <= end; i++){
9395                 nodes.push(ns[i]);
9396             }
9397         } else{
9398             for(var i = start; i >= end; i--){
9399                 nodes.push(ns[i]);
9400             }
9401         }
9402         return nodes;
9403     },
9404
9405     /**
9406      * Finds the index of the passed node
9407      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9408      * @return {Number} The index of the node or -1
9409      */
9410     indexOf : function(node){
9411         node = this.getNode(node);
9412         if(typeof node.nodeIndex == "number"){
9413             return node.nodeIndex;
9414         }
9415         var ns = this.nodes;
9416         for(var i = 0, len = ns.length; i < len; i++){
9417             if(ns[i] == node){
9418                 return i;
9419             }
9420         }
9421         return -1;
9422     }
9423 });
9424 /*
9425  * Based on:
9426  * Ext JS Library 1.1.1
9427  * Copyright(c) 2006-2007, Ext JS, LLC.
9428  *
9429  * Originally Released Under LGPL - original licence link has changed is not relivant.
9430  *
9431  * Fork - LGPL
9432  * <script type="text/javascript">
9433  */
9434
9435 /**
9436  * @class Roo.JsonView
9437  * @extends Roo.View
9438  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9439 <pre><code>
9440 var view = new Roo.JsonView("my-element",
9441     '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9442     { multiSelect: true, jsonRoot: "data" }
9443 );
9444
9445 // listen for node click?
9446 view.on("click", function(vw, index, node, e){
9447     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9448 });
9449
9450 // direct load of JSON data
9451 view.load("foobar.php");
9452
9453 // Example from my blog list
9454 var tpl = new Roo.Template(
9455     '&lt;div class="entry"&gt;' +
9456     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9457     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9458     "&lt;/div&gt;&lt;hr /&gt;"
9459 );
9460
9461 var moreView = new Roo.JsonView("entry-list", tpl, {
9462     jsonRoot: "posts"
9463 });
9464 moreView.on("beforerender", this.sortEntries, this);
9465 moreView.load({
9466     url: "/blog/get-posts.php",
9467     params: "allposts=true",
9468     text: "Loading Blog Entries..."
9469 });
9470 </code></pre>
9471  * @constructor
9472  * Create a new JsonView
9473  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
9474  * @param {Template} tpl The rendering template
9475  * @param {Object} config The config object
9476  */
9477 Roo.JsonView = function(container, tpl, config){
9478     Roo.JsonView.superclass.constructor.call(this, container, tpl, config);
9479
9480     var um = this.el.getUpdateManager();
9481     um.setRenderer(this);
9482     um.on("update", this.onLoad, this);
9483     um.on("failure", this.onLoadException, this);
9484
9485     /**
9486      * @event beforerender
9487      * Fires before rendering of the downloaded JSON data.
9488      * @param {Roo.JsonView} this
9489      * @param {Object} data The JSON data loaded
9490      */
9491     /**
9492      * @event load
9493      * Fires when data is loaded.
9494      * @param {Roo.JsonView} this
9495      * @param {Object} data The JSON data loaded
9496      * @param {Object} response The raw Connect response object
9497      */
9498     /**
9499      * @event loadexception
9500      * Fires when loading fails.
9501      * @param {Roo.JsonView} this
9502      * @param {Object} response The raw Connect response object
9503      */
9504     this.addEvents({
9505         'beforerender' : true,
9506         'load' : true,
9507         'loadexception' : true
9508     });
9509 };
9510 Roo.extend(Roo.JsonView, Roo.View, {
9511     /**
9512      * The root property in the loaded JSON object that contains the data
9513      * @type {String}
9514      */
9515     jsonRoot : "",
9516
9517     /**
9518      * Refreshes the view.
9519      */
9520     refresh : function(){
9521         this.clearSelections();
9522         this.el.update("");
9523         var html = [];
9524         var o = this.jsonData;
9525         if(o && o.length > 0){
9526             for(var i = 0, len = o.length; i < len; i++){
9527                 var data = this.prepareData(o[i], i, o);
9528                 html[html.length] = this.tpl.apply(data);
9529             }
9530         }else{
9531             html.push(this.emptyText);
9532         }
9533         this.el.update(html.join(""));
9534         this.nodes = this.el.dom.childNodes;
9535         this.updateIndexes(0);
9536     },
9537
9538     /**
9539      * 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.
9540      * @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:
9541      <pre><code>
9542      view.load({
9543          url: "your-url.php",
9544          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9545          callback: yourFunction,
9546          scope: yourObject, //(optional scope)
9547          discardUrl: false,
9548          nocache: false,
9549          text: "Loading...",
9550          timeout: 30,
9551          scripts: false
9552      });
9553      </code></pre>
9554      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9555      * 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.
9556      * @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}
9557      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9558      * @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.
9559      */
9560     load : function(){
9561         var um = this.el.getUpdateManager();
9562         um.update.apply(um, arguments);
9563     },
9564
9565     render : function(el, response){
9566         this.clearSelections();
9567         this.el.update("");
9568         var o;
9569         try{
9570             o = Roo.util.JSON.decode(response.responseText);
9571             if(this.jsonRoot){
9572                 
9573                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
9574             }
9575         } catch(e){
9576         }
9577         /**
9578          * The current JSON data or null
9579          */
9580         this.jsonData = o;
9581         this.beforeRender();
9582         this.refresh();
9583     },
9584
9585 /**
9586  * Get the number of records in the current JSON dataset
9587  * @return {Number}
9588  */
9589     getCount : function(){
9590         return this.jsonData ? this.jsonData.length : 0;
9591     },
9592
9593 /**
9594  * Returns the JSON object for the specified node(s)
9595  * @param {HTMLElement/Array} node The node or an array of nodes
9596  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9597  * you get the JSON object for the node
9598  */
9599     getNodeData : function(node){
9600         if(node instanceof Array){
9601             var data = [];
9602             for(var i = 0, len = node.length; i < len; i++){
9603                 data.push(this.getNodeData(node[i]));
9604             }
9605             return data;
9606         }
9607         return this.jsonData[this.indexOf(node)] || null;
9608     },
9609
9610     beforeRender : function(){
9611         this.snapshot = this.jsonData;
9612         if(this.sortInfo){
9613             this.sort.apply(this, this.sortInfo);
9614         }
9615         this.fireEvent("beforerender", this, this.jsonData);
9616     },
9617
9618     onLoad : function(el, o){
9619         this.fireEvent("load", this, this.jsonData, o);
9620     },
9621
9622     onLoadException : function(el, o){
9623         this.fireEvent("loadexception", this, o);
9624     },
9625
9626 /**
9627  * Filter the data by a specific property.
9628  * @param {String} property A property on your JSON objects
9629  * @param {String/RegExp} value Either string that the property values
9630  * should start with, or a RegExp to test against the property
9631  */
9632     filter : function(property, value){
9633         if(this.jsonData){
9634             var data = [];
9635             var ss = this.snapshot;
9636             if(typeof value == "string"){
9637                 var vlen = value.length;
9638                 if(vlen == 0){
9639                     this.clearFilter();
9640                     return;
9641                 }
9642                 value = value.toLowerCase();
9643                 for(var i = 0, len = ss.length; i < len; i++){
9644                     var o = ss[i];
9645                     if(o[property].substr(0, vlen).toLowerCase() == value){
9646                         data.push(o);
9647                     }
9648                 }
9649             } else if(value.exec){ // regex?
9650                 for(var i = 0, len = ss.length; i < len; i++){
9651                     var o = ss[i];
9652                     if(value.test(o[property])){
9653                         data.push(o);
9654                     }
9655                 }
9656             } else{
9657                 return;
9658             }
9659             this.jsonData = data;
9660             this.refresh();
9661         }
9662     },
9663
9664 /**
9665  * Filter by a function. The passed function will be called with each
9666  * object in the current dataset. If the function returns true the value is kept,
9667  * otherwise it is filtered.
9668  * @param {Function} fn
9669  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9670  */
9671     filterBy : function(fn, scope){
9672         if(this.jsonData){
9673             var data = [];
9674             var ss = this.snapshot;
9675             for(var i = 0, len = ss.length; i < len; i++){
9676                 var o = ss[i];
9677                 if(fn.call(scope || this, o)){
9678                     data.push(o);
9679                 }
9680             }
9681             this.jsonData = data;
9682             this.refresh();
9683         }
9684     },
9685
9686 /**
9687  * Clears the current filter.
9688  */
9689     clearFilter : function(){
9690         if(this.snapshot && this.jsonData != this.snapshot){
9691             this.jsonData = this.snapshot;
9692             this.refresh();
9693         }
9694     },
9695
9696
9697 /**
9698  * Sorts the data for this view and refreshes it.
9699  * @param {String} property A property on your JSON objects to sort on
9700  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9701  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9702  */
9703     sort : function(property, dir, sortType){
9704         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9705         if(this.jsonData){
9706             var p = property;
9707             var dsc = dir && dir.toLowerCase() == "desc";
9708             var f = function(o1, o2){
9709                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9710                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9711                 ;
9712                 if(v1 < v2){
9713                     return dsc ? +1 : -1;
9714                 } else if(v1 > v2){
9715                     return dsc ? -1 : +1;
9716                 } else{
9717                     return 0;
9718                 }
9719             };
9720             this.jsonData.sort(f);
9721             this.refresh();
9722             if(this.jsonData != this.snapshot){
9723                 this.snapshot.sort(f);
9724             }
9725         }
9726     }
9727 });/*
9728  * Based on:
9729  * Ext JS Library 1.1.1
9730  * Copyright(c) 2006-2007, Ext JS, LLC.
9731  *
9732  * Originally Released Under LGPL - original licence link has changed is not relivant.
9733  *
9734  * Fork - LGPL
9735  * <script type="text/javascript">
9736  */
9737  
9738
9739 /**
9740  * @class Roo.ColorPalette
9741  * @extends Roo.Component
9742  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9743  * Here's an example of typical usage:
9744  * <pre><code>
9745 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9746 cp.render('my-div');
9747
9748 cp.on('select', function(palette, selColor){
9749     // do something with selColor
9750 });
9751 </code></pre>
9752  * @constructor
9753  * Create a new ColorPalette
9754  * @param {Object} config The config object
9755  */
9756 Roo.ColorPalette = function(config){
9757     Roo.ColorPalette.superclass.constructor.call(this, config);
9758     this.addEvents({
9759         /**
9760              * @event select
9761              * Fires when a color is selected
9762              * @param {ColorPalette} this
9763              * @param {String} color The 6-digit color hex code (without the # symbol)
9764              */
9765         select: true
9766     });
9767
9768     if(this.handler){
9769         this.on("select", this.handler, this.scope, true);
9770     }
9771 };
9772 Roo.extend(Roo.ColorPalette, Roo.Component, {
9773     /**
9774      * @cfg {String} itemCls
9775      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9776      */
9777     itemCls : "x-color-palette",
9778     /**
9779      * @cfg {String} value
9780      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9781      * the hex codes are case-sensitive.
9782      */
9783     value : null,
9784     clickEvent:'click',
9785     // private
9786     ctype: "Roo.ColorPalette",
9787
9788     /**
9789      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9790      */
9791     allowReselect : false,
9792
9793     /**
9794      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9795      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9796      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9797      * of colors with the width setting until the box is symmetrical.</p>
9798      * <p>You can override individual colors if needed:</p>
9799      * <pre><code>
9800 var cp = new Roo.ColorPalette();
9801 cp.colors[0] = "FF0000";  // change the first box to red
9802 </code></pre>
9803
9804 Or you can provide a custom array of your own for complete control:
9805 <pre><code>
9806 var cp = new Roo.ColorPalette();
9807 cp.colors = ["000000", "993300", "333300"];
9808 </code></pre>
9809      * @type Array
9810      */
9811     colors : [
9812         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9813         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9814         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9815         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9816         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9817     ],
9818
9819     // private
9820     onRender : function(container, position){
9821         var t = new Roo.MasterTemplate(
9822             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9823         );
9824         var c = this.colors;
9825         for(var i = 0, len = c.length; i < len; i++){
9826             t.add([c[i]]);
9827         }
9828         var el = document.createElement("div");
9829         el.className = this.itemCls;
9830         t.overwrite(el);
9831         container.dom.insertBefore(el, position);
9832         this.el = Roo.get(el);
9833         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9834         if(this.clickEvent != 'click'){
9835             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9836         }
9837     },
9838
9839     // private
9840     afterRender : function(){
9841         Roo.ColorPalette.superclass.afterRender.call(this);
9842         if(this.value){
9843             var s = this.value;
9844             this.value = null;
9845             this.select(s);
9846         }
9847     },
9848
9849     // private
9850     handleClick : function(e, t){
9851         e.preventDefault();
9852         if(!this.disabled){
9853             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9854             this.select(c.toUpperCase());
9855         }
9856     },
9857
9858     /**
9859      * Selects the specified color in the palette (fires the select event)
9860      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9861      */
9862     select : function(color){
9863         color = color.replace("#", "");
9864         if(color != this.value || this.allowReselect){
9865             var el = this.el;
9866             if(this.value){
9867                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9868             }
9869             el.child("a.color-"+color).addClass("x-color-palette-sel");
9870             this.value = color;
9871             this.fireEvent("select", this, color);
9872         }
9873     }
9874 });/*
9875  * Based on:
9876  * Ext JS Library 1.1.1
9877  * Copyright(c) 2006-2007, Ext JS, LLC.
9878  *
9879  * Originally Released Under LGPL - original licence link has changed is not relivant.
9880  *
9881  * Fork - LGPL
9882  * <script type="text/javascript">
9883  */
9884  
9885 /**
9886  * @class Roo.DatePicker
9887  * @extends Roo.Component
9888  * Simple date picker class.
9889  * @constructor
9890  * Create a new DatePicker
9891  * @param {Object} config The config object
9892  */
9893 Roo.DatePicker = function(config){
9894     Roo.DatePicker.superclass.constructor.call(this, config);
9895
9896     this.value = config && config.value ?
9897                  config.value.clearTime() : new Date().clearTime();
9898
9899     this.addEvents({
9900         /**
9901              * @event select
9902              * Fires when a date is selected
9903              * @param {DatePicker} this
9904              * @param {Date} date The selected date
9905              */
9906         select: true
9907     });
9908
9909     if(this.handler){
9910         this.on("select", this.handler,  this.scope || this);
9911     }
9912     // build the disabledDatesRE
9913     if(!this.disabledDatesRE && this.disabledDates){
9914         var dd = this.disabledDates;
9915         var re = "(?:";
9916         for(var i = 0; i < dd.length; i++){
9917             re += dd[i];
9918             if(i != dd.length-1) re += "|";
9919         }
9920         this.disabledDatesRE = new RegExp(re + ")");
9921     }
9922 };
9923
9924 Roo.extend(Roo.DatePicker, Roo.Component, {
9925     /**
9926      * @cfg {String} todayText
9927      * The text to display on the button that selects the current date (defaults to "Today")
9928      */
9929     todayText : "Today",
9930     /**
9931      * @cfg {String} okText
9932      * The text to display on the ok button
9933      */
9934     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9935     /**
9936      * @cfg {String} cancelText
9937      * The text to display on the cancel button
9938      */
9939     cancelText : "Cancel",
9940     /**
9941      * @cfg {String} todayTip
9942      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9943      */
9944     todayTip : "{0} (Spacebar)",
9945     /**
9946      * @cfg {Date} minDate
9947      * Minimum allowable date (JavaScript date object, defaults to null)
9948      */
9949     minDate : null,
9950     /**
9951      * @cfg {Date} maxDate
9952      * Maximum allowable date (JavaScript date object, defaults to null)
9953      */
9954     maxDate : null,
9955     /**
9956      * @cfg {String} minText
9957      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9958      */
9959     minText : "This date is before the minimum date",
9960     /**
9961      * @cfg {String} maxText
9962      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9963      */
9964     maxText : "This date is after the maximum date",
9965     /**
9966      * @cfg {String} format
9967      * The default date format string which can be overriden for localization support.  The format must be
9968      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9969      */
9970     format : "m/d/y",
9971     /**
9972      * @cfg {Array} disabledDays
9973      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9974      */
9975     disabledDays : null,
9976     /**
9977      * @cfg {String} disabledDaysText
9978      * The tooltip to display when the date falls on a disabled day (defaults to "")
9979      */
9980     disabledDaysText : "",
9981     /**
9982      * @cfg {RegExp} disabledDatesRE
9983      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9984      */
9985     disabledDatesRE : null,
9986     /**
9987      * @cfg {String} disabledDatesText
9988      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9989      */
9990     disabledDatesText : "",
9991     /**
9992      * @cfg {Boolean} constrainToViewport
9993      * True to constrain the date picker to the viewport (defaults to true)
9994      */
9995     constrainToViewport : true,
9996     /**
9997      * @cfg {Array} monthNames
9998      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9999      */
10000     monthNames : Date.monthNames,
10001     /**
10002      * @cfg {Array} dayNames
10003      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10004      */
10005     dayNames : Date.dayNames,
10006     /**
10007      * @cfg {String} nextText
10008      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10009      */
10010     nextText: 'Next Month (Control+Right)',
10011     /**
10012      * @cfg {String} prevText
10013      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10014      */
10015     prevText: 'Previous Month (Control+Left)',
10016     /**
10017      * @cfg {String} monthYearText
10018      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10019      */
10020     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10021     /**
10022      * @cfg {Number} startDay
10023      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10024      */
10025     startDay : 0,
10026     /**
10027      * @cfg {Bool} showClear
10028      * Show a clear button (usefull for date form elements that can be blank.)
10029      */
10030     
10031     showClear: false,
10032     
10033     /**
10034      * Sets the value of the date field
10035      * @param {Date} value The date to set
10036      */
10037     setValue : function(value){
10038         var old = this.value;
10039         this.value = value.clearTime(true);
10040         if(this.el){
10041             this.update(this.value);
10042         }
10043     },
10044
10045     /**
10046      * Gets the current selected value of the date field
10047      * @return {Date} The selected date
10048      */
10049     getValue : function(){
10050         return this.value;
10051     },
10052
10053     // private
10054     focus : function(){
10055         if(this.el){
10056             this.update(this.activeDate);
10057         }
10058     },
10059
10060     // private
10061     onRender : function(container, position){
10062         var m = [
10063              '<table cellspacing="0">',
10064                 '<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>',
10065                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10066         var dn = this.dayNames;
10067         for(var i = 0; i < 7; i++){
10068             var d = this.startDay+i;
10069             if(d > 6){
10070                 d = d-7;
10071             }
10072             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10073         }
10074         m[m.length] = "</tr></thead><tbody><tr>";
10075         for(var i = 0; i < 42; i++) {
10076             if(i % 7 == 0 && i != 0){
10077                 m[m.length] = "</tr><tr>";
10078             }
10079             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10080         }
10081         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10082             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10083
10084         var el = document.createElement("div");
10085         el.className = "x-date-picker";
10086         el.innerHTML = m.join("");
10087
10088         container.dom.insertBefore(el, position);
10089
10090         this.el = Roo.get(el);
10091         this.eventEl = Roo.get(el.firstChild);
10092
10093         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10094             handler: this.showPrevMonth,
10095             scope: this,
10096             preventDefault:true,
10097             stopDefault:true
10098         });
10099
10100         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10101             handler: this.showNextMonth,
10102             scope: this,
10103             preventDefault:true,
10104             stopDefault:true
10105         });
10106
10107         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10108
10109         this.monthPicker = this.el.down('div.x-date-mp');
10110         this.monthPicker.enableDisplayMode('block');
10111         
10112         var kn = new Roo.KeyNav(this.eventEl, {
10113             "left" : function(e){
10114                 e.ctrlKey ?
10115                     this.showPrevMonth() :
10116                     this.update(this.activeDate.add("d", -1));
10117             },
10118
10119             "right" : function(e){
10120                 e.ctrlKey ?
10121                     this.showNextMonth() :
10122                     this.update(this.activeDate.add("d", 1));
10123             },
10124
10125             "up" : function(e){
10126                 e.ctrlKey ?
10127                     this.showNextYear() :
10128                     this.update(this.activeDate.add("d", -7));
10129             },
10130
10131             "down" : function(e){
10132                 e.ctrlKey ?
10133                     this.showPrevYear() :
10134                     this.update(this.activeDate.add("d", 7));
10135             },
10136
10137             "pageUp" : function(e){
10138                 this.showNextMonth();
10139             },
10140
10141             "pageDown" : function(e){
10142                 this.showPrevMonth();
10143             },
10144
10145             "enter" : function(e){
10146                 e.stopPropagation();
10147                 return true;
10148             },
10149
10150             scope : this
10151         });
10152
10153         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10154
10155         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10156
10157         this.el.unselectable();
10158         
10159         this.cells = this.el.select("table.x-date-inner tbody td");
10160         this.textNodes = this.el.query("table.x-date-inner tbody span");
10161
10162         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10163             text: "&#160;",
10164             tooltip: this.monthYearText
10165         });
10166
10167         this.mbtn.on('click', this.showMonthPicker, this);
10168         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10169
10170
10171         var today = (new Date()).dateFormat(this.format);
10172         
10173         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10174         baseTb.add({
10175             text: String.format(this.todayText, today),
10176             tooltip: String.format(this.todayTip, today),
10177             handler: this.selectToday,
10178             scope: this
10179         });
10180         
10181         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10182             
10183         //});
10184         if (this.showClear) {
10185             
10186             baseTb.add( new Roo.Toolbar.Fill());
10187             baseTb.add({
10188                 text: '&#160;',
10189                 cls: 'x-btn-icon x-btn-clear',
10190                 handler: function() {
10191                     //this.value = '';
10192                     this.fireEvent("select", this, '');
10193                 },
10194                 scope: this
10195             });
10196         }
10197         
10198         
10199         if(Roo.isIE){
10200             this.el.repaint();
10201         }
10202         this.update(this.value);
10203     },
10204
10205     createMonthPicker : function(){
10206         if(!this.monthPicker.dom.firstChild){
10207             var buf = ['<table border="0" cellspacing="0">'];
10208             for(var i = 0; i < 6; i++){
10209                 buf.push(
10210                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10211                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10212                     i == 0 ?
10213                     '<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>' :
10214                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10215                 );
10216             }
10217             buf.push(
10218                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10219                     this.okText,
10220                     '</button><button type="button" class="x-date-mp-cancel">',
10221                     this.cancelText,
10222                     '</button></td></tr>',
10223                 '</table>'
10224             );
10225             this.monthPicker.update(buf.join(''));
10226             this.monthPicker.on('click', this.onMonthClick, this);
10227             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10228
10229             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10230             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10231
10232             this.mpMonths.each(function(m, a, i){
10233                 i += 1;
10234                 if((i%2) == 0){
10235                     m.dom.xmonth = 5 + Math.round(i * .5);
10236                 }else{
10237                     m.dom.xmonth = Math.round((i-1) * .5);
10238                 }
10239             });
10240         }
10241     },
10242
10243     showMonthPicker : function(){
10244         this.createMonthPicker();
10245         var size = this.el.getSize();
10246         this.monthPicker.setSize(size);
10247         this.monthPicker.child('table').setSize(size);
10248
10249         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10250         this.updateMPMonth(this.mpSelMonth);
10251         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10252         this.updateMPYear(this.mpSelYear);
10253
10254         this.monthPicker.slideIn('t', {duration:.2});
10255     },
10256
10257     updateMPYear : function(y){
10258         this.mpyear = y;
10259         var ys = this.mpYears.elements;
10260         for(var i = 1; i <= 10; i++){
10261             var td = ys[i-1], y2;
10262             if((i%2) == 0){
10263                 y2 = y + Math.round(i * .5);
10264                 td.firstChild.innerHTML = y2;
10265                 td.xyear = y2;
10266             }else{
10267                 y2 = y - (5-Math.round(i * .5));
10268                 td.firstChild.innerHTML = y2;
10269                 td.xyear = y2;
10270             }
10271             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10272         }
10273     },
10274
10275     updateMPMonth : function(sm){
10276         this.mpMonths.each(function(m, a, i){
10277             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10278         });
10279     },
10280
10281     selectMPMonth: function(m){
10282         
10283     },
10284
10285     onMonthClick : function(e, t){
10286         e.stopEvent();
10287         var el = new Roo.Element(t), pn;
10288         if(el.is('button.x-date-mp-cancel')){
10289             this.hideMonthPicker();
10290         }
10291         else if(el.is('button.x-date-mp-ok')){
10292             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10293             this.hideMonthPicker();
10294         }
10295         else if(pn = el.up('td.x-date-mp-month', 2)){
10296             this.mpMonths.removeClass('x-date-mp-sel');
10297             pn.addClass('x-date-mp-sel');
10298             this.mpSelMonth = pn.dom.xmonth;
10299         }
10300         else if(pn = el.up('td.x-date-mp-year', 2)){
10301             this.mpYears.removeClass('x-date-mp-sel');
10302             pn.addClass('x-date-mp-sel');
10303             this.mpSelYear = pn.dom.xyear;
10304         }
10305         else if(el.is('a.x-date-mp-prev')){
10306             this.updateMPYear(this.mpyear-10);
10307         }
10308         else if(el.is('a.x-date-mp-next')){
10309             this.updateMPYear(this.mpyear+10);
10310         }
10311     },
10312
10313     onMonthDblClick : function(e, t){
10314         e.stopEvent();
10315         var el = new Roo.Element(t), pn;
10316         if(pn = el.up('td.x-date-mp-month', 2)){
10317             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10318             this.hideMonthPicker();
10319         }
10320         else if(pn = el.up('td.x-date-mp-year', 2)){
10321             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10322             this.hideMonthPicker();
10323         }
10324     },
10325
10326     hideMonthPicker : function(disableAnim){
10327         if(this.monthPicker){
10328             if(disableAnim === true){
10329                 this.monthPicker.hide();
10330             }else{
10331                 this.monthPicker.slideOut('t', {duration:.2});
10332             }
10333         }
10334     },
10335
10336     // private
10337     showPrevMonth : function(e){
10338         this.update(this.activeDate.add("mo", -1));
10339     },
10340
10341     // private
10342     showNextMonth : function(e){
10343         this.update(this.activeDate.add("mo", 1));
10344     },
10345
10346     // private
10347     showPrevYear : function(){
10348         this.update(this.activeDate.add("y", -1));
10349     },
10350
10351     // private
10352     showNextYear : function(){
10353         this.update(this.activeDate.add("y", 1));
10354     },
10355
10356     // private
10357     handleMouseWheel : function(e){
10358         var delta = e.getWheelDelta();
10359         if(delta > 0){
10360             this.showPrevMonth();
10361             e.stopEvent();
10362         } else if(delta < 0){
10363             this.showNextMonth();
10364             e.stopEvent();
10365         }
10366     },
10367
10368     // private
10369     handleDateClick : function(e, t){
10370         e.stopEvent();
10371         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10372             this.setValue(new Date(t.dateValue));
10373             this.fireEvent("select", this, this.value);
10374         }
10375     },
10376
10377     // private
10378     selectToday : function(){
10379         this.setValue(new Date().clearTime());
10380         this.fireEvent("select", this, this.value);
10381     },
10382
10383     // private
10384     update : function(date){
10385         var vd = this.activeDate;
10386         this.activeDate = date;
10387         if(vd && this.el){
10388             var t = date.getTime();
10389             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10390                 this.cells.removeClass("x-date-selected");
10391                 this.cells.each(function(c){
10392                    if(c.dom.firstChild.dateValue == t){
10393                        c.addClass("x-date-selected");
10394                        setTimeout(function(){
10395                             try{c.dom.firstChild.focus();}catch(e){}
10396                        }, 50);
10397                        return false;
10398                    }
10399                 });
10400                 return;
10401             }
10402         }
10403         var days = date.getDaysInMonth();
10404         var firstOfMonth = date.getFirstDateOfMonth();
10405         var startingPos = firstOfMonth.getDay()-this.startDay;
10406
10407         if(startingPos <= this.startDay){
10408             startingPos += 7;
10409         }
10410
10411         var pm = date.add("mo", -1);
10412         var prevStart = pm.getDaysInMonth()-startingPos;
10413
10414         var cells = this.cells.elements;
10415         var textEls = this.textNodes;
10416         days += startingPos;
10417
10418         // convert everything to numbers so it's fast
10419         var day = 86400000;
10420         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10421         var today = new Date().clearTime().getTime();
10422         var sel = date.clearTime().getTime();
10423         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10424         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10425         var ddMatch = this.disabledDatesRE;
10426         var ddText = this.disabledDatesText;
10427         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10428         var ddaysText = this.disabledDaysText;
10429         var format = this.format;
10430
10431         var setCellClass = function(cal, cell){
10432             cell.title = "";
10433             var t = d.getTime();
10434             cell.firstChild.dateValue = t;
10435             if(t == today){
10436                 cell.className += " x-date-today";
10437                 cell.title = cal.todayText;
10438             }
10439             if(t == sel){
10440                 cell.className += " x-date-selected";
10441                 setTimeout(function(){
10442                     try{cell.firstChild.focus();}catch(e){}
10443                 }, 50);
10444             }
10445             // disabling
10446             if(t < min) {
10447                 cell.className = " x-date-disabled";
10448                 cell.title = cal.minText;
10449                 return;
10450             }
10451             if(t > max) {
10452                 cell.className = " x-date-disabled";
10453                 cell.title = cal.maxText;
10454                 return;
10455             }
10456             if(ddays){
10457                 if(ddays.indexOf(d.getDay()) != -1){
10458                     cell.title = ddaysText;
10459                     cell.className = " x-date-disabled";
10460                 }
10461             }
10462             if(ddMatch && format){
10463                 var fvalue = d.dateFormat(format);
10464                 if(ddMatch.test(fvalue)){
10465                     cell.title = ddText.replace("%0", fvalue);
10466                     cell.className = " x-date-disabled";
10467                 }
10468             }
10469         };
10470
10471         var i = 0;
10472         for(; i < startingPos; i++) {
10473             textEls[i].innerHTML = (++prevStart);
10474             d.setDate(d.getDate()+1);
10475             cells[i].className = "x-date-prevday";
10476             setCellClass(this, cells[i]);
10477         }
10478         for(; i < days; i++){
10479             intDay = i - startingPos + 1;
10480             textEls[i].innerHTML = (intDay);
10481             d.setDate(d.getDate()+1);
10482             cells[i].className = "x-date-active";
10483             setCellClass(this, cells[i]);
10484         }
10485         var extraDays = 0;
10486         for(; i < 42; i++) {
10487              textEls[i].innerHTML = (++extraDays);
10488              d.setDate(d.getDate()+1);
10489              cells[i].className = "x-date-nextday";
10490              setCellClass(this, cells[i]);
10491         }
10492
10493         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10494
10495         if(!this.internalRender){
10496             var main = this.el.dom.firstChild;
10497             var w = main.offsetWidth;
10498             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10499             Roo.fly(main).setWidth(w);
10500             this.internalRender = true;
10501             // opera does not respect the auto grow header center column
10502             // then, after it gets a width opera refuses to recalculate
10503             // without a second pass
10504             if(Roo.isOpera && !this.secondPass){
10505                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10506                 this.secondPass = true;
10507                 this.update.defer(10, this, [date]);
10508             }
10509         }
10510     }
10511 });/*
10512  * Based on:
10513  * Ext JS Library 1.1.1
10514  * Copyright(c) 2006-2007, Ext JS, LLC.
10515  *
10516  * Originally Released Under LGPL - original licence link has changed is not relivant.
10517  *
10518  * Fork - LGPL
10519  * <script type="text/javascript">
10520  */
10521 /**
10522  * @class Roo.TabPanel
10523  * @extends Roo.util.Observable
10524  * A lightweight tab container.
10525  * <br><br>
10526  * Usage:
10527  * <pre><code>
10528 // basic tabs 1, built from existing content
10529 var tabs = new Roo.TabPanel("tabs1");
10530 tabs.addTab("script", "View Script");
10531 tabs.addTab("markup", "View Markup");
10532 tabs.activate("script");
10533
10534 // more advanced tabs, built from javascript
10535 var jtabs = new Roo.TabPanel("jtabs");
10536 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10537
10538 // set up the UpdateManager
10539 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10540 var updater = tab2.getUpdateManager();
10541 updater.setDefaultUrl("ajax1.htm");
10542 tab2.on('activate', updater.refresh, updater, true);
10543
10544 // Use setUrl for Ajax loading
10545 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10546 tab3.setUrl("ajax2.htm", null, true);
10547
10548 // Disabled tab
10549 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10550 tab4.disable();
10551
10552 jtabs.activate("jtabs-1");
10553  * </code></pre>
10554  * @constructor
10555  * Create a new TabPanel.
10556  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10557  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10558  */
10559 Roo.TabPanel = function(container, config){
10560     /**
10561     * The container element for this TabPanel.
10562     * @type Roo.Element
10563     */
10564     this.el = Roo.get(container, true);
10565     if(config){
10566         if(typeof config == "boolean"){
10567             this.tabPosition = config ? "bottom" : "top";
10568         }else{
10569             Roo.apply(this, config);
10570         }
10571     }
10572     if(this.tabPosition == "bottom"){
10573         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10574         this.el.addClass("x-tabs-bottom");
10575     }
10576     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10577     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10578     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10579     if(Roo.isIE){
10580         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10581     }
10582     if(this.tabPosition != "bottom"){
10583     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10584      * @type Roo.Element
10585      */
10586       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10587       this.el.addClass("x-tabs-top");
10588     }
10589     this.items = [];
10590
10591     this.bodyEl.setStyle("position", "relative");
10592
10593     this.active = null;
10594     this.activateDelegate = this.activate.createDelegate(this);
10595
10596     this.addEvents({
10597         /**
10598          * @event tabchange
10599          * Fires when the active tab changes
10600          * @param {Roo.TabPanel} this
10601          * @param {Roo.TabPanelItem} activePanel The new active tab
10602          */
10603         "tabchange": true,
10604         /**
10605          * @event beforetabchange
10606          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10607          * @param {Roo.TabPanel} this
10608          * @param {Object} e Set cancel to true on this object to cancel the tab change
10609          * @param {Roo.TabPanelItem} tab The tab being changed to
10610          */
10611         "beforetabchange" : true
10612     });
10613
10614     Roo.EventManager.onWindowResize(this.onResize, this);
10615     this.cpad = this.el.getPadding("lr");
10616     this.hiddenCount = 0;
10617
10618     Roo.TabPanel.superclass.constructor.call(this);
10619 };
10620
10621 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10622         /*
10623          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10624          */
10625     tabPosition : "top",
10626         /*
10627          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10628          */
10629     currentTabWidth : 0,
10630         /*
10631          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10632          */
10633     minTabWidth : 40,
10634         /*
10635          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10636          */
10637     maxTabWidth : 250,
10638         /*
10639          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10640          */
10641     preferredTabWidth : 175,
10642         /*
10643          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10644          */
10645     resizeTabs : false,
10646         /*
10647          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10648          */
10649     monitorResize : true,
10650
10651     /**
10652      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10653      * @param {String} id The id of the div to use <b>or create</b>
10654      * @param {String} text The text for the tab
10655      * @param {String} content (optional) Content to put in the TabPanelItem body
10656      * @param {Boolean} closable (optional) True to create a close icon on the tab
10657      * @return {Roo.TabPanelItem} The created TabPanelItem
10658      */
10659     addTab : function(id, text, content, closable){
10660         var item = new Roo.TabPanelItem(this, id, text, closable);
10661         this.addTabItem(item);
10662         if(content){
10663             item.setContent(content);
10664         }
10665         return item;
10666     },
10667
10668     /**
10669      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10670      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10671      * @return {Roo.TabPanelItem}
10672      */
10673     getTab : function(id){
10674         return this.items[id];
10675     },
10676
10677     /**
10678      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10679      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10680      */
10681     hideTab : function(id){
10682         var t = this.items[id];
10683         if(!t.isHidden()){
10684            t.setHidden(true);
10685            this.hiddenCount++;
10686            this.autoSizeTabs();
10687         }
10688     },
10689
10690     /**
10691      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10692      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10693      */
10694     unhideTab : function(id){
10695         var t = this.items[id];
10696         if(t.isHidden()){
10697            t.setHidden(false);
10698            this.hiddenCount--;
10699            this.autoSizeTabs();
10700         }
10701     },
10702
10703     /**
10704      * Adds an existing {@link Roo.TabPanelItem}.
10705      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10706      */
10707     addTabItem : function(item){
10708         this.items[item.id] = item;
10709         this.items.push(item);
10710         if(this.resizeTabs){
10711            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10712            this.autoSizeTabs();
10713         }else{
10714             item.autoSize();
10715         }
10716     },
10717
10718     /**
10719      * Removes a {@link Roo.TabPanelItem}.
10720      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10721      */
10722     removeTab : function(id){
10723         var items = this.items;
10724         var tab = items[id];
10725         if(!tab) return;
10726         var index = items.indexOf(tab);
10727         if(this.active == tab && items.length > 1){
10728             var newTab = this.getNextAvailable(index);
10729             if(newTab)newTab.activate();
10730         }
10731         this.stripEl.dom.removeChild(tab.pnode.dom);
10732         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10733             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10734         }
10735         items.splice(index, 1);
10736         delete this.items[tab.id];
10737         tab.fireEvent("close", tab);
10738         tab.purgeListeners();
10739         this.autoSizeTabs();
10740     },
10741
10742     getNextAvailable : function(start){
10743         var items = this.items;
10744         var index = start;
10745         // look for a next tab that will slide over to
10746         // replace the one being removed
10747         while(index < items.length){
10748             var item = items[++index];
10749             if(item && !item.isHidden()){
10750                 return item;
10751             }
10752         }
10753         // if one isn't found select the previous tab (on the left)
10754         index = start;
10755         while(index >= 0){
10756             var item = items[--index];
10757             if(item && !item.isHidden()){
10758                 return item;
10759             }
10760         }
10761         return null;
10762     },
10763
10764     /**
10765      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10766      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10767      */
10768     disableTab : function(id){
10769         var tab = this.items[id];
10770         if(tab && this.active != tab){
10771             tab.disable();
10772         }
10773     },
10774
10775     /**
10776      * Enables a {@link Roo.TabPanelItem} that is disabled.
10777      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10778      */
10779     enableTab : function(id){
10780         var tab = this.items[id];
10781         tab.enable();
10782     },
10783
10784     /**
10785      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10786      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10787      * @return {Roo.TabPanelItem} The TabPanelItem.
10788      */
10789     activate : function(id){
10790         var tab = this.items[id];
10791         if(!tab){
10792             return null;
10793         }
10794         if(tab == this.active || tab.disabled){
10795             return tab;
10796         }
10797         var e = {};
10798         this.fireEvent("beforetabchange", this, e, tab);
10799         if(e.cancel !== true && !tab.disabled){
10800             if(this.active){
10801                 this.active.hide();
10802             }
10803             this.active = this.items[id];
10804             this.active.show();
10805             this.fireEvent("tabchange", this, this.active);
10806         }
10807         return tab;
10808     },
10809
10810     /**
10811      * Gets the active {@link Roo.TabPanelItem}.
10812      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10813      */
10814     getActiveTab : function(){
10815         return this.active;
10816     },
10817
10818     /**
10819      * Updates the tab body element to fit the height of the container element
10820      * for overflow scrolling
10821      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10822      */
10823     syncHeight : function(targetHeight){
10824         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10825         var bm = this.bodyEl.getMargins();
10826         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10827         this.bodyEl.setHeight(newHeight);
10828         return newHeight;
10829     },
10830
10831     onResize : function(){
10832         if(this.monitorResize){
10833             this.autoSizeTabs();
10834         }
10835     },
10836
10837     /**
10838      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10839      */
10840     beginUpdate : function(){
10841         this.updating = true;
10842     },
10843
10844     /**
10845      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10846      */
10847     endUpdate : function(){
10848         this.updating = false;
10849         this.autoSizeTabs();
10850     },
10851
10852     /**
10853      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10854      */
10855     autoSizeTabs : function(){
10856         var count = this.items.length;
10857         var vcount = count - this.hiddenCount;
10858         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10859         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10860         var availWidth = Math.floor(w / vcount);
10861         var b = this.stripBody;
10862         if(b.getWidth() > w){
10863             var tabs = this.items;
10864             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10865             if(availWidth < this.minTabWidth){
10866                 /*if(!this.sleft){    // incomplete scrolling code
10867                     this.createScrollButtons();
10868                 }
10869                 this.showScroll();
10870                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10871             }
10872         }else{
10873             if(this.currentTabWidth < this.preferredTabWidth){
10874                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10875             }
10876         }
10877     },
10878
10879     /**
10880      * Returns the number of tabs in this TabPanel.
10881      * @return {Number}
10882      */
10883      getCount : function(){
10884          return this.items.length;
10885      },
10886
10887     /**
10888      * Resizes all the tabs to the passed width
10889      * @param {Number} The new width
10890      */
10891     setTabWidth : function(width){
10892         this.currentTabWidth = width;
10893         for(var i = 0, len = this.items.length; i < len; i++) {
10894                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10895         }
10896     },
10897
10898     /**
10899      * Destroys this TabPanel
10900      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10901      */
10902     destroy : function(removeEl){
10903         Roo.EventManager.removeResizeListener(this.onResize, this);
10904         for(var i = 0, len = this.items.length; i < len; i++){
10905             this.items[i].purgeListeners();
10906         }
10907         if(removeEl === true){
10908             this.el.update("");
10909             this.el.remove();
10910         }
10911     }
10912 });
10913
10914 /**
10915  * @class Roo.TabPanelItem
10916  * @extends Roo.util.Observable
10917  * Represents an individual item (tab plus body) in a TabPanel.
10918  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10919  * @param {String} id The id of this TabPanelItem
10920  * @param {String} text The text for the tab of this TabPanelItem
10921  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10922  */
10923 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10924     /**
10925      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10926      * @type Roo.TabPanel
10927      */
10928     this.tabPanel = tabPanel;
10929     /**
10930      * The id for this TabPanelItem
10931      * @type String
10932      */
10933     this.id = id;
10934     /** @private */
10935     this.disabled = false;
10936     /** @private */
10937     this.text = text;
10938     /** @private */
10939     this.loaded = false;
10940     this.closable = closable;
10941
10942     /**
10943      * The body element for this TabPanelItem.
10944      * @type Roo.Element
10945      */
10946     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10947     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10948     this.bodyEl.setStyle("display", "block");
10949     this.bodyEl.setStyle("zoom", "1");
10950     this.hideAction();
10951
10952     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10953     /** @private */
10954     this.el = Roo.get(els.el, true);
10955     this.inner = Roo.get(els.inner, true);
10956     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10957     this.pnode = Roo.get(els.el.parentNode, true);
10958     this.el.on("mousedown", this.onTabMouseDown, this);
10959     this.el.on("click", this.onTabClick, this);
10960     /** @private */
10961     if(closable){
10962         var c = Roo.get(els.close, true);
10963         c.dom.title = this.closeText;
10964         c.addClassOnOver("close-over");
10965         c.on("click", this.closeClick, this);
10966      }
10967
10968     this.addEvents({
10969          /**
10970          * @event activate
10971          * Fires when this tab becomes the active tab.
10972          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10973          * @param {Roo.TabPanelItem} this
10974          */
10975         "activate": true,
10976         /**
10977          * @event beforeclose
10978          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10979          * @param {Roo.TabPanelItem} this
10980          * @param {Object} e Set cancel to true on this object to cancel the close.
10981          */
10982         "beforeclose": true,
10983         /**
10984          * @event close
10985          * Fires when this tab is closed.
10986          * @param {Roo.TabPanelItem} this
10987          */
10988          "close": true,
10989         /**
10990          * @event deactivate
10991          * Fires when this tab is no longer the active tab.
10992          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10993          * @param {Roo.TabPanelItem} this
10994          */
10995          "deactivate" : true
10996     });
10997     this.hidden = false;
10998
10999     Roo.TabPanelItem.superclass.constructor.call(this);
11000 };
11001
11002 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11003     purgeListeners : function(){
11004        Roo.util.Observable.prototype.purgeListeners.call(this);
11005        this.el.removeAllListeners();
11006     },
11007     /**
11008      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11009      */
11010     show : function(){
11011         this.pnode.addClass("on");
11012         this.showAction();
11013         if(Roo.isOpera){
11014             this.tabPanel.stripWrap.repaint();
11015         }
11016         this.fireEvent("activate", this.tabPanel, this);
11017     },
11018
11019     /**
11020      * Returns true if this tab is the active tab.
11021      * @return {Boolean}
11022      */
11023     isActive : function(){
11024         return this.tabPanel.getActiveTab() == this;
11025     },
11026
11027     /**
11028      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11029      */
11030     hide : function(){
11031         this.pnode.removeClass("on");
11032         this.hideAction();
11033         this.fireEvent("deactivate", this.tabPanel, this);
11034     },
11035
11036     hideAction : function(){
11037         this.bodyEl.hide();
11038         this.bodyEl.setStyle("position", "absolute");
11039         this.bodyEl.setLeft("-20000px");
11040         this.bodyEl.setTop("-20000px");
11041     },
11042
11043     showAction : function(){
11044         this.bodyEl.setStyle("position", "relative");
11045         this.bodyEl.setTop("");
11046         this.bodyEl.setLeft("");
11047         this.bodyEl.show();
11048     },
11049
11050     /**
11051      * Set the tooltip for the tab.
11052      * @param {String} tooltip The tab's tooltip
11053      */
11054     setTooltip : function(text){
11055         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11056             this.textEl.dom.qtip = text;
11057             this.textEl.dom.removeAttribute('title');
11058         }else{
11059             this.textEl.dom.title = text;
11060         }
11061     },
11062
11063     onTabClick : function(e){
11064         e.preventDefault();
11065         this.tabPanel.activate(this.id);
11066     },
11067
11068     onTabMouseDown : function(e){
11069         e.preventDefault();
11070         this.tabPanel.activate(this.id);
11071     },
11072
11073     getWidth : function(){
11074         return this.inner.getWidth();
11075     },
11076
11077     setWidth : function(width){
11078         var iwidth = width - this.pnode.getPadding("lr");
11079         this.inner.setWidth(iwidth);
11080         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11081         this.pnode.setWidth(width);
11082     },
11083
11084     /**
11085      * Show or hide the tab
11086      * @param {Boolean} hidden True to hide or false to show.
11087      */
11088     setHidden : function(hidden){
11089         this.hidden = hidden;
11090         this.pnode.setStyle("display", hidden ? "none" : "");
11091     },
11092
11093     /**
11094      * Returns true if this tab is "hidden"
11095      * @return {Boolean}
11096      */
11097     isHidden : function(){
11098         return this.hidden;
11099     },
11100
11101     /**
11102      * Returns the text for this tab
11103      * @return {String}
11104      */
11105     getText : function(){
11106         return this.text;
11107     },
11108
11109     autoSize : function(){
11110         //this.el.beginMeasure();
11111         this.textEl.setWidth(1);
11112         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11113         //this.el.endMeasure();
11114     },
11115
11116     /**
11117      * Sets the text for the tab (Note: this also sets the tooltip text)
11118      * @param {String} text The tab's text and tooltip
11119      */
11120     setText : function(text){
11121         this.text = text;
11122         this.textEl.update(text);
11123         this.setTooltip(text);
11124         if(!this.tabPanel.resizeTabs){
11125             this.autoSize();
11126         }
11127     },
11128     /**
11129      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11130      */
11131     activate : function(){
11132         this.tabPanel.activate(this.id);
11133     },
11134
11135     /**
11136      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11137      */
11138     disable : function(){
11139         if(this.tabPanel.active != this){
11140             this.disabled = true;
11141             this.pnode.addClass("disabled");
11142         }
11143     },
11144
11145     /**
11146      * Enables this TabPanelItem if it was previously disabled.
11147      */
11148     enable : function(){
11149         this.disabled = false;
11150         this.pnode.removeClass("disabled");
11151     },
11152
11153     /**
11154      * Sets the content for this TabPanelItem.
11155      * @param {String} content The content
11156      * @param {Boolean} loadScripts true to look for and load scripts
11157      */
11158     setContent : function(content, loadScripts){
11159         this.bodyEl.update(content, loadScripts);
11160     },
11161
11162     /**
11163      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11164      * @return {Roo.UpdateManager} The UpdateManager
11165      */
11166     getUpdateManager : function(){
11167         return this.bodyEl.getUpdateManager();
11168     },
11169
11170     /**
11171      * Set a URL to be used to load the content for this TabPanelItem.
11172      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11173      * @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)
11174      * @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)
11175      * @return {Roo.UpdateManager} The UpdateManager
11176      */
11177     setUrl : function(url, params, loadOnce){
11178         if(this.refreshDelegate){
11179             this.un('activate', this.refreshDelegate);
11180         }
11181         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11182         this.on("activate", this.refreshDelegate);
11183         return this.bodyEl.getUpdateManager();
11184     },
11185
11186     /** @private */
11187     _handleRefresh : function(url, params, loadOnce){
11188         if(!loadOnce || !this.loaded){
11189             var updater = this.bodyEl.getUpdateManager();
11190             updater.update(url, params, this._setLoaded.createDelegate(this));
11191         }
11192     },
11193
11194     /**
11195      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11196      *   Will fail silently if the setUrl method has not been called.
11197      *   This does not activate the panel, just updates its content.
11198      */
11199     refresh : function(){
11200         if(this.refreshDelegate){
11201            this.loaded = false;
11202            this.refreshDelegate();
11203         }
11204     },
11205
11206     /** @private */
11207     _setLoaded : function(){
11208         this.loaded = true;
11209     },
11210
11211     /** @private */
11212     closeClick : function(e){
11213         var o = {};
11214         e.stopEvent();
11215         this.fireEvent("beforeclose", this, o);
11216         if(o.cancel !== true){
11217             this.tabPanel.removeTab(this.id);
11218         }
11219     },
11220     /**
11221      * The text displayed in the tooltip for the close icon.
11222      * @type String
11223      */
11224     closeText : "Close this tab"
11225 });
11226
11227 /** @private */
11228 Roo.TabPanel.prototype.createStrip = function(container){
11229     var strip = document.createElement("div");
11230     strip.className = "x-tabs-wrap";
11231     container.appendChild(strip);
11232     return strip;
11233 };
11234 /** @private */
11235 Roo.TabPanel.prototype.createStripList = function(strip){
11236     // div wrapper for retard IE
11237     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>';
11238     return strip.firstChild.firstChild.firstChild.firstChild;
11239 };
11240 /** @private */
11241 Roo.TabPanel.prototype.createBody = function(container){
11242     var body = document.createElement("div");
11243     Roo.id(body, "tab-body");
11244     Roo.fly(body).addClass("x-tabs-body");
11245     container.appendChild(body);
11246     return body;
11247 };
11248 /** @private */
11249 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11250     var body = Roo.getDom(id);
11251     if(!body){
11252         body = document.createElement("div");
11253         body.id = id;
11254     }
11255     Roo.fly(body).addClass("x-tabs-item-body");
11256     bodyEl.insertBefore(body, bodyEl.firstChild);
11257     return body;
11258 };
11259 /** @private */
11260 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11261     var td = document.createElement("td");
11262     stripEl.appendChild(td);
11263     if(closable){
11264         td.className = "x-tabs-closable";
11265         if(!this.closeTpl){
11266             this.closeTpl = new Roo.Template(
11267                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11268                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11269                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11270             );
11271         }
11272         var el = this.closeTpl.overwrite(td, {"text": text});
11273         var close = el.getElementsByTagName("div")[0];
11274         var inner = el.getElementsByTagName("em")[0];
11275         return {"el": el, "close": close, "inner": inner};
11276     } else {
11277         if(!this.tabTpl){
11278             this.tabTpl = new Roo.Template(
11279                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11280                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11281             );
11282         }
11283         var el = this.tabTpl.overwrite(td, {"text": text});
11284         var inner = el.getElementsByTagName("em")[0];
11285         return {"el": el, "inner": inner};
11286     }
11287 };/*
11288  * Based on:
11289  * Ext JS Library 1.1.1
11290  * Copyright(c) 2006-2007, Ext JS, LLC.
11291  *
11292  * Originally Released Under LGPL - original licence link has changed is not relivant.
11293  *
11294  * Fork - LGPL
11295  * <script type="text/javascript">
11296  */
11297
11298 /**
11299  * @class Roo.Button
11300  * @extends Roo.util.Observable
11301  * Simple Button class
11302  * @cfg {String} text The button text
11303  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11304  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11305  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11306  * @cfg {Object} scope The scope of the handler
11307  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11308  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11309  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11310  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11311  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11312  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11313    applies if enableToggle = true)
11314  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11315  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11316   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11317  * @constructor
11318  * Create a new button
11319  * @param {Object} config The config object
11320  */
11321 Roo.Button = function(renderTo, config)
11322 {
11323     if (!config) {
11324         config = renderTo;
11325         renderTo = config.renderTo || false;
11326     }
11327     
11328     Roo.apply(this, config);
11329     this.addEvents({
11330         /**
11331              * @event click
11332              * Fires when this button is clicked
11333              * @param {Button} this
11334              * @param {EventObject} e The click event
11335              */
11336             "click" : true,
11337         /**
11338              * @event toggle
11339              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11340              * @param {Button} this
11341              * @param {Boolean} pressed
11342              */
11343             "toggle" : true,
11344         /**
11345              * @event mouseover
11346              * Fires when the mouse hovers over the button
11347              * @param {Button} this
11348              * @param {Event} e The event object
11349              */
11350         'mouseover' : true,
11351         /**
11352              * @event mouseout
11353              * Fires when the mouse exits the button
11354              * @param {Button} this
11355              * @param {Event} e The event object
11356              */
11357         'mouseout': true,
11358          /**
11359              * @event render
11360              * Fires when the button is rendered
11361              * @param {Button} this
11362              */
11363         'render': true
11364     });
11365     if(this.menu){
11366         this.menu = Roo.menu.MenuMgr.get(this.menu);
11367     }
11368     if(renderTo){
11369         this.render(renderTo);
11370     }
11371     
11372     Roo.util.Observable.call(this);
11373 };
11374
11375 Roo.extend(Roo.Button, Roo.util.Observable, {
11376     /**
11377      * 
11378      */
11379     
11380     /**
11381      * Read-only. True if this button is hidden
11382      * @type Boolean
11383      */
11384     hidden : false,
11385     /**
11386      * Read-only. True if this button is disabled
11387      * @type Boolean
11388      */
11389     disabled : false,
11390     /**
11391      * Read-only. True if this button is pressed (only if enableToggle = true)
11392      * @type Boolean
11393      */
11394     pressed : false,
11395
11396     /**
11397      * @cfg {Number} tabIndex 
11398      * The DOM tabIndex for this button (defaults to undefined)
11399      */
11400     tabIndex : undefined,
11401
11402     /**
11403      * @cfg {Boolean} enableToggle
11404      * True to enable pressed/not pressed toggling (defaults to false)
11405      */
11406     enableToggle: false,
11407     /**
11408      * @cfg {Mixed} menu
11409      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11410      */
11411     menu : undefined,
11412     /**
11413      * @cfg {String} menuAlign
11414      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11415      */
11416     menuAlign : "tl-bl?",
11417
11418     /**
11419      * @cfg {String} iconCls
11420      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11421      */
11422     iconCls : undefined,
11423     /**
11424      * @cfg {String} type
11425      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11426      */
11427     type : 'button',
11428
11429     // private
11430     menuClassTarget: 'tr',
11431
11432     /**
11433      * @cfg {String} clickEvent
11434      * The type of event to map to the button's event handler (defaults to 'click')
11435      */
11436     clickEvent : 'click',
11437
11438     /**
11439      * @cfg {Boolean} handleMouseEvents
11440      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11441      */
11442     handleMouseEvents : true,
11443
11444     /**
11445      * @cfg {String} tooltipType
11446      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11447      */
11448     tooltipType : 'qtip',
11449
11450     /**
11451      * @cfg {String} cls
11452      * A CSS class to apply to the button's main element.
11453      */
11454     
11455     /**
11456      * @cfg {Roo.Template} template (Optional)
11457      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11458      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11459      * require code modifications if required elements (e.g. a button) aren't present.
11460      */
11461
11462     // private
11463     render : function(renderTo){
11464         var btn;
11465         if(this.hideParent){
11466             this.parentEl = Roo.get(renderTo);
11467         }
11468         if(!this.dhconfig){
11469             if(!this.template){
11470                 if(!Roo.Button.buttonTemplate){
11471                     // hideous table template
11472                     Roo.Button.buttonTemplate = new Roo.Template(
11473                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11474                         '<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>',
11475                         "</tr></tbody></table>");
11476                 }
11477                 this.template = Roo.Button.buttonTemplate;
11478             }
11479             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11480             var btnEl = btn.child("button:first");
11481             btnEl.on('focus', this.onFocus, this);
11482             btnEl.on('blur', this.onBlur, this);
11483             if(this.cls){
11484                 btn.addClass(this.cls);
11485             }
11486             if(this.icon){
11487                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11488             }
11489             if(this.iconCls){
11490                 btnEl.addClass(this.iconCls);
11491                 if(!this.cls){
11492                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11493                 }
11494             }
11495             if(this.tabIndex !== undefined){
11496                 btnEl.dom.tabIndex = this.tabIndex;
11497             }
11498             if(this.tooltip){
11499                 if(typeof this.tooltip == 'object'){
11500                     Roo.QuickTips.tips(Roo.apply({
11501                           target: btnEl.id
11502                     }, this.tooltip));
11503                 } else {
11504                     btnEl.dom[this.tooltipType] = this.tooltip;
11505                 }
11506             }
11507         }else{
11508             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11509         }
11510         this.el = btn;
11511         if(this.id){
11512             this.el.dom.id = this.el.id = this.id;
11513         }
11514         if(this.menu){
11515             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11516             this.menu.on("show", this.onMenuShow, this);
11517             this.menu.on("hide", this.onMenuHide, this);
11518         }
11519         btn.addClass("x-btn");
11520         if(Roo.isIE && !Roo.isIE7){
11521             this.autoWidth.defer(1, this);
11522         }else{
11523             this.autoWidth();
11524         }
11525         if(this.handleMouseEvents){
11526             btn.on("mouseover", this.onMouseOver, this);
11527             btn.on("mouseout", this.onMouseOut, this);
11528             btn.on("mousedown", this.onMouseDown, this);
11529         }
11530         btn.on(this.clickEvent, this.onClick, this);
11531         //btn.on("mouseup", this.onMouseUp, this);
11532         if(this.hidden){
11533             this.hide();
11534         }
11535         if(this.disabled){
11536             this.disable();
11537         }
11538         Roo.ButtonToggleMgr.register(this);
11539         if(this.pressed){
11540             this.el.addClass("x-btn-pressed");
11541         }
11542         if(this.repeat){
11543             var repeater = new Roo.util.ClickRepeater(btn,
11544                 typeof this.repeat == "object" ? this.repeat : {}
11545             );
11546             repeater.on("click", this.onClick,  this);
11547         }
11548         this.fireEvent('render', this);
11549         
11550     },
11551     /**
11552      * Returns the button's underlying element
11553      * @return {Roo.Element} The element
11554      */
11555     getEl : function(){
11556         return this.el;  
11557     },
11558     
11559     /**
11560      * Destroys this Button and removes any listeners.
11561      */
11562     destroy : function(){
11563         Roo.ButtonToggleMgr.unregister(this);
11564         this.el.removeAllListeners();
11565         this.purgeListeners();
11566         this.el.remove();
11567     },
11568
11569     // private
11570     autoWidth : function(){
11571         if(this.el){
11572             this.el.setWidth("auto");
11573             if(Roo.isIE7 && Roo.isStrict){
11574                 var ib = this.el.child('button');
11575                 if(ib && ib.getWidth() > 20){
11576                     ib.clip();
11577                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11578                 }
11579             }
11580             if(this.minWidth){
11581                 if(this.hidden){
11582                     this.el.beginMeasure();
11583                 }
11584                 if(this.el.getWidth() < this.minWidth){
11585                     this.el.setWidth(this.minWidth);
11586                 }
11587                 if(this.hidden){
11588                     this.el.endMeasure();
11589                 }
11590             }
11591         }
11592     },
11593
11594     /**
11595      * Assigns this button's click handler
11596      * @param {Function} handler The function to call when the button is clicked
11597      * @param {Object} scope (optional) Scope for the function passed in
11598      */
11599     setHandler : function(handler, scope){
11600         this.handler = handler;
11601         this.scope = scope;  
11602     },
11603     
11604     /**
11605      * Sets this button's text
11606      * @param {String} text The button text
11607      */
11608     setText : function(text){
11609         this.text = text;
11610         if(this.el){
11611             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11612         }
11613         this.autoWidth();
11614     },
11615     
11616     /**
11617      * Gets the text for this button
11618      * @return {String} The button text
11619      */
11620     getText : function(){
11621         return this.text;  
11622     },
11623     
11624     /**
11625      * Show this button
11626      */
11627     show: function(){
11628         this.hidden = false;
11629         if(this.el){
11630             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11631         }
11632     },
11633     
11634     /**
11635      * Hide this button
11636      */
11637     hide: function(){
11638         this.hidden = true;
11639         if(this.el){
11640             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11641         }
11642     },
11643     
11644     /**
11645      * Convenience function for boolean show/hide
11646      * @param {Boolean} visible True to show, false to hide
11647      */
11648     setVisible: function(visible){
11649         if(visible) {
11650             this.show();
11651         }else{
11652             this.hide();
11653         }
11654     },
11655     
11656     /**
11657      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11658      * @param {Boolean} state (optional) Force a particular state
11659      */
11660     toggle : function(state){
11661         state = state === undefined ? !this.pressed : state;
11662         if(state != this.pressed){
11663             if(state){
11664                 this.el.addClass("x-btn-pressed");
11665                 this.pressed = true;
11666                 this.fireEvent("toggle", this, true);
11667             }else{
11668                 this.el.removeClass("x-btn-pressed");
11669                 this.pressed = false;
11670                 this.fireEvent("toggle", this, false);
11671             }
11672             if(this.toggleHandler){
11673                 this.toggleHandler.call(this.scope || this, this, state);
11674             }
11675         }
11676     },
11677     
11678     /**
11679      * Focus the button
11680      */
11681     focus : function(){
11682         this.el.child('button:first').focus();
11683     },
11684     
11685     /**
11686      * Disable this button
11687      */
11688     disable : function(){
11689         if(this.el){
11690             this.el.addClass("x-btn-disabled");
11691         }
11692         this.disabled = true;
11693     },
11694     
11695     /**
11696      * Enable this button
11697      */
11698     enable : function(){
11699         if(this.el){
11700             this.el.removeClass("x-btn-disabled");
11701         }
11702         this.disabled = false;
11703     },
11704
11705     /**
11706      * Convenience function for boolean enable/disable
11707      * @param {Boolean} enabled True to enable, false to disable
11708      */
11709     setDisabled : function(v){
11710         this[v !== true ? "enable" : "disable"]();
11711     },
11712
11713     // private
11714     onClick : function(e){
11715         if(e){
11716             e.preventDefault();
11717         }
11718         if(e.button != 0){
11719             return;
11720         }
11721         if(!this.disabled){
11722             if(this.enableToggle){
11723                 this.toggle();
11724             }
11725             if(this.menu && !this.menu.isVisible()){
11726                 this.menu.show(this.el, this.menuAlign);
11727             }
11728             this.fireEvent("click", this, e);
11729             if(this.handler){
11730                 this.el.removeClass("x-btn-over");
11731                 this.handler.call(this.scope || this, this, e);
11732             }
11733         }
11734     },
11735     // private
11736     onMouseOver : function(e){
11737         if(!this.disabled){
11738             this.el.addClass("x-btn-over");
11739             this.fireEvent('mouseover', this, e);
11740         }
11741     },
11742     // private
11743     onMouseOut : function(e){
11744         if(!e.within(this.el,  true)){
11745             this.el.removeClass("x-btn-over");
11746             this.fireEvent('mouseout', this, e);
11747         }
11748     },
11749     // private
11750     onFocus : function(e){
11751         if(!this.disabled){
11752             this.el.addClass("x-btn-focus");
11753         }
11754     },
11755     // private
11756     onBlur : function(e){
11757         this.el.removeClass("x-btn-focus");
11758     },
11759     // private
11760     onMouseDown : function(e){
11761         if(!this.disabled && e.button == 0){
11762             this.el.addClass("x-btn-click");
11763             Roo.get(document).on('mouseup', this.onMouseUp, this);
11764         }
11765     },
11766     // private
11767     onMouseUp : function(e){
11768         if(e.button == 0){
11769             this.el.removeClass("x-btn-click");
11770             Roo.get(document).un('mouseup', this.onMouseUp, this);
11771         }
11772     },
11773     // private
11774     onMenuShow : function(e){
11775         this.el.addClass("x-btn-menu-active");
11776     },
11777     // private
11778     onMenuHide : function(e){
11779         this.el.removeClass("x-btn-menu-active");
11780     }   
11781 });
11782
11783 // Private utility class used by Button
11784 Roo.ButtonToggleMgr = function(){
11785    var groups = {};
11786    
11787    function toggleGroup(btn, state){
11788        if(state){
11789            var g = groups[btn.toggleGroup];
11790            for(var i = 0, l = g.length; i < l; i++){
11791                if(g[i] != btn){
11792                    g[i].toggle(false);
11793                }
11794            }
11795        }
11796    }
11797    
11798    return {
11799        register : function(btn){
11800            if(!btn.toggleGroup){
11801                return;
11802            }
11803            var g = groups[btn.toggleGroup];
11804            if(!g){
11805                g = groups[btn.toggleGroup] = [];
11806            }
11807            g.push(btn);
11808            btn.on("toggle", toggleGroup);
11809        },
11810        
11811        unregister : function(btn){
11812            if(!btn.toggleGroup){
11813                return;
11814            }
11815            var g = groups[btn.toggleGroup];
11816            if(g){
11817                g.remove(btn);
11818                btn.un("toggle", toggleGroup);
11819            }
11820        }
11821    };
11822 }();/*
11823  * Based on:
11824  * Ext JS Library 1.1.1
11825  * Copyright(c) 2006-2007, Ext JS, LLC.
11826  *
11827  * Originally Released Under LGPL - original licence link has changed is not relivant.
11828  *
11829  * Fork - LGPL
11830  * <script type="text/javascript">
11831  */
11832  
11833 /**
11834  * @class Roo.SplitButton
11835  * @extends Roo.Button
11836  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11837  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11838  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11839  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11840  * @cfg {String} arrowTooltip The title attribute of the arrow
11841  * @constructor
11842  * Create a new menu button
11843  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11844  * @param {Object} config The config object
11845  */
11846 Roo.SplitButton = function(renderTo, config){
11847     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11848     /**
11849      * @event arrowclick
11850      * Fires when this button's arrow is clicked
11851      * @param {SplitButton} this
11852      * @param {EventObject} e The click event
11853      */
11854     this.addEvents({"arrowclick":true});
11855 };
11856
11857 Roo.extend(Roo.SplitButton, Roo.Button, {
11858     render : function(renderTo){
11859         // this is one sweet looking template!
11860         var tpl = new Roo.Template(
11861             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11862             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11863             '<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>',
11864             "</tbody></table></td><td>",
11865             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11866             '<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>',
11867             "</tbody></table></td></tr></table>"
11868         );
11869         var btn = tpl.append(renderTo, [this.text, this.type], true);
11870         var btnEl = btn.child("button");
11871         if(this.cls){
11872             btn.addClass(this.cls);
11873         }
11874         if(this.icon){
11875             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11876         }
11877         if(this.iconCls){
11878             btnEl.addClass(this.iconCls);
11879             if(!this.cls){
11880                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11881             }
11882         }
11883         this.el = btn;
11884         if(this.handleMouseEvents){
11885             btn.on("mouseover", this.onMouseOver, this);
11886             btn.on("mouseout", this.onMouseOut, this);
11887             btn.on("mousedown", this.onMouseDown, this);
11888             btn.on("mouseup", this.onMouseUp, this);
11889         }
11890         btn.on(this.clickEvent, this.onClick, this);
11891         if(this.tooltip){
11892             if(typeof this.tooltip == 'object'){
11893                 Roo.QuickTips.tips(Roo.apply({
11894                       target: btnEl.id
11895                 }, this.tooltip));
11896             } else {
11897                 btnEl.dom[this.tooltipType] = this.tooltip;
11898             }
11899         }
11900         if(this.arrowTooltip){
11901             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11902         }
11903         if(this.hidden){
11904             this.hide();
11905         }
11906         if(this.disabled){
11907             this.disable();
11908         }
11909         if(this.pressed){
11910             this.el.addClass("x-btn-pressed");
11911         }
11912         if(Roo.isIE && !Roo.isIE7){
11913             this.autoWidth.defer(1, this);
11914         }else{
11915             this.autoWidth();
11916         }
11917         if(this.menu){
11918             this.menu.on("show", this.onMenuShow, this);
11919             this.menu.on("hide", this.onMenuHide, this);
11920         }
11921         this.fireEvent('render', this);
11922     },
11923
11924     // private
11925     autoWidth : function(){
11926         if(this.el){
11927             var tbl = this.el.child("table:first");
11928             var tbl2 = this.el.child("table:last");
11929             this.el.setWidth("auto");
11930             tbl.setWidth("auto");
11931             if(Roo.isIE7 && Roo.isStrict){
11932                 var ib = this.el.child('button:first');
11933                 if(ib && ib.getWidth() > 20){
11934                     ib.clip();
11935                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11936                 }
11937             }
11938             if(this.minWidth){
11939                 if(this.hidden){
11940                     this.el.beginMeasure();
11941                 }
11942                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11943                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11944                 }
11945                 if(this.hidden){
11946                     this.el.endMeasure();
11947                 }
11948             }
11949             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11950         } 
11951     },
11952     /**
11953      * Sets this button's click handler
11954      * @param {Function} handler The function to call when the button is clicked
11955      * @param {Object} scope (optional) Scope for the function passed above
11956      */
11957     setHandler : function(handler, scope){
11958         this.handler = handler;
11959         this.scope = scope;  
11960     },
11961     
11962     /**
11963      * Sets this button's arrow click handler
11964      * @param {Function} handler The function to call when the arrow is clicked
11965      * @param {Object} scope (optional) Scope for the function passed above
11966      */
11967     setArrowHandler : function(handler, scope){
11968         this.arrowHandler = handler;
11969         this.scope = scope;  
11970     },
11971     
11972     /**
11973      * Focus the button
11974      */
11975     focus : function(){
11976         if(this.el){
11977             this.el.child("button:first").focus();
11978         }
11979     },
11980
11981     // private
11982     onClick : function(e){
11983         e.preventDefault();
11984         if(!this.disabled){
11985             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11986                 if(this.menu && !this.menu.isVisible()){
11987                     this.menu.show(this.el, this.menuAlign);
11988                 }
11989                 this.fireEvent("arrowclick", this, e);
11990                 if(this.arrowHandler){
11991                     this.arrowHandler.call(this.scope || this, this, e);
11992                 }
11993             }else{
11994                 this.fireEvent("click", this, e);
11995                 if(this.handler){
11996                     this.handler.call(this.scope || this, this, e);
11997                 }
11998             }
11999         }
12000     },
12001     // private
12002     onMouseDown : function(e){
12003         if(!this.disabled){
12004             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12005         }
12006     },
12007     // private
12008     onMouseUp : function(e){
12009         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12010     }   
12011 });
12012
12013
12014 // backwards compat
12015 Roo.MenuButton = Roo.SplitButton;/*
12016  * Based on:
12017  * Ext JS Library 1.1.1
12018  * Copyright(c) 2006-2007, Ext JS, LLC.
12019  *
12020  * Originally Released Under LGPL - original licence link has changed is not relivant.
12021  *
12022  * Fork - LGPL
12023  * <script type="text/javascript">
12024  */
12025
12026 /**
12027  * @class Roo.Toolbar
12028  * Basic Toolbar class.
12029  * @constructor
12030  * Creates a new Toolbar
12031  * @param {Object} config The config object
12032  */ 
12033 Roo.Toolbar = function(container, buttons, config)
12034 {
12035     /// old consturctor format still supported..
12036     if(container instanceof Array){ // omit the container for later rendering
12037         buttons = container;
12038         config = buttons;
12039         container = null;
12040     }
12041     if (typeof(container) == 'object' && container.xtype) {
12042         config = container;
12043         container = config.container;
12044         buttons = config.buttons; // not really - use items!!
12045     }
12046     var xitems = [];
12047     if (config && config.items) {
12048         xitems = config.items;
12049         delete config.items;
12050     }
12051     Roo.apply(this, config);
12052     this.buttons = buttons;
12053     
12054     if(container){
12055         this.render(container);
12056     }
12057     Roo.each(xitems, function(b) {
12058         this.add(b);
12059     }, this);
12060     
12061 };
12062
12063 Roo.Toolbar.prototype = {
12064     /**
12065      * @cfg {Roo.data.Store} items
12066      * array of button configs or elements to add
12067      */
12068     
12069     /**
12070      * @cfg {String/HTMLElement/Element} container
12071      * The id or element that will contain the toolbar
12072      */
12073     // private
12074     render : function(ct){
12075         this.el = Roo.get(ct);
12076         if(this.cls){
12077             this.el.addClass(this.cls);
12078         }
12079         // using a table allows for vertical alignment
12080         // 100% width is needed by Safari...
12081         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12082         this.tr = this.el.child("tr", true);
12083         var autoId = 0;
12084         this.items = new Roo.util.MixedCollection(false, function(o){
12085             return o.id || ("item" + (++autoId));
12086         });
12087         if(this.buttons){
12088             this.add.apply(this, this.buttons);
12089             delete this.buttons;
12090         }
12091     },
12092
12093     /**
12094      * Adds element(s) to the toolbar -- this function takes a variable number of 
12095      * arguments of mixed type and adds them to the toolbar.
12096      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12097      * <ul>
12098      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12099      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12100      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12101      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12102      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12103      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12104      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12105      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12106      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12107      * </ul>
12108      * @param {Mixed} arg2
12109      * @param {Mixed} etc.
12110      */
12111     add : function(){
12112         var a = arguments, l = a.length;
12113         for(var i = 0; i < l; i++){
12114             this._add(a[i]);
12115         }
12116     },
12117     // private..
12118     _add : function(el) {
12119         
12120         if (el.xtype) {
12121             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12122         }
12123         
12124         if (el.applyTo){ // some kind of form field
12125             return this.addField(el);
12126         } 
12127         if (el.render){ // some kind of Toolbar.Item
12128             return this.addItem(el);
12129         }
12130         if (typeof el == "string"){ // string
12131             if(el == "separator" || el == "-"){
12132                 return this.addSeparator();
12133             }
12134             if (el == " "){
12135                 return this.addSpacer();
12136             }
12137             if(el == "->"){
12138                 return this.addFill();
12139             }
12140             return this.addText(el);
12141             
12142         }
12143         if(el.tagName){ // element
12144             return this.addElement(el);
12145         }
12146         if(typeof el == "object"){ // must be button config?
12147             return this.addButton(el);
12148         }
12149         // and now what?!?!
12150         return false;
12151         
12152     },
12153     
12154     /**
12155      * Add an Xtype element
12156      * @param {Object} xtype Xtype Object
12157      * @return {Object} created Object
12158      */
12159     addxtype : function(e){
12160         return this.add(e);  
12161     },
12162     
12163     /**
12164      * Returns the Element for this toolbar.
12165      * @return {Roo.Element}
12166      */
12167     getEl : function(){
12168         return this.el;  
12169     },
12170     
12171     /**
12172      * Adds a separator
12173      * @return {Roo.Toolbar.Item} The separator item
12174      */
12175     addSeparator : function(){
12176         return this.addItem(new Roo.Toolbar.Separator());
12177     },
12178
12179     /**
12180      * Adds a spacer element
12181      * @return {Roo.Toolbar.Spacer} The spacer item
12182      */
12183     addSpacer : function(){
12184         return this.addItem(new Roo.Toolbar.Spacer());
12185     },
12186
12187     /**
12188      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12189      * @return {Roo.Toolbar.Fill} The fill item
12190      */
12191     addFill : function(){
12192         return this.addItem(new Roo.Toolbar.Fill());
12193     },
12194
12195     /**
12196      * Adds any standard HTML element to the toolbar
12197      * @param {String/HTMLElement/Element} el The element or id of the element to add
12198      * @return {Roo.Toolbar.Item} The element's item
12199      */
12200     addElement : function(el){
12201         return this.addItem(new Roo.Toolbar.Item(el));
12202     },
12203     /**
12204      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12205      * @type Roo.util.MixedCollection  
12206      */
12207     items : false,
12208      
12209     /**
12210      * Adds any Toolbar.Item or subclass
12211      * @param {Roo.Toolbar.Item} item
12212      * @return {Roo.Toolbar.Item} The item
12213      */
12214     addItem : function(item){
12215         var td = this.nextBlock();
12216         item.render(td);
12217         this.items.add(item);
12218         return item;
12219     },
12220     
12221     /**
12222      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12223      * @param {Object/Array} config A button config or array of configs
12224      * @return {Roo.Toolbar.Button/Array}
12225      */
12226     addButton : function(config){
12227         if(config instanceof Array){
12228             var buttons = [];
12229             for(var i = 0, len = config.length; i < len; i++) {
12230                 buttons.push(this.addButton(config[i]));
12231             }
12232             return buttons;
12233         }
12234         var b = config;
12235         if(!(config instanceof Roo.Toolbar.Button)){
12236             b = config.split ?
12237                 new Roo.Toolbar.SplitButton(config) :
12238                 new Roo.Toolbar.Button(config);
12239         }
12240         var td = this.nextBlock();
12241         b.render(td);
12242         this.items.add(b);
12243         return b;
12244     },
12245     
12246     /**
12247      * Adds text to the toolbar
12248      * @param {String} text The text to add
12249      * @return {Roo.Toolbar.Item} The element's item
12250      */
12251     addText : function(text){
12252         return this.addItem(new Roo.Toolbar.TextItem(text));
12253     },
12254     
12255     /**
12256      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12257      * @param {Number} index The index where the item is to be inserted
12258      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12259      * @return {Roo.Toolbar.Button/Item}
12260      */
12261     insertButton : function(index, item){
12262         if(item instanceof Array){
12263             var buttons = [];
12264             for(var i = 0, len = item.length; i < len; i++) {
12265                buttons.push(this.insertButton(index + i, item[i]));
12266             }
12267             return buttons;
12268         }
12269         if (!(item instanceof Roo.Toolbar.Button)){
12270            item = new Roo.Toolbar.Button(item);
12271         }
12272         var td = document.createElement("td");
12273         this.tr.insertBefore(td, this.tr.childNodes[index]);
12274         item.render(td);
12275         this.items.insert(index, item);
12276         return item;
12277     },
12278     
12279     /**
12280      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12281      * @param {Object} config
12282      * @return {Roo.Toolbar.Item} The element's item
12283      */
12284     addDom : function(config, returnEl){
12285         var td = this.nextBlock();
12286         Roo.DomHelper.overwrite(td, config);
12287         var ti = new Roo.Toolbar.Item(td.firstChild);
12288         ti.render(td);
12289         this.items.add(ti);
12290         return ti;
12291     },
12292
12293     /**
12294      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12295      * @type Roo.util.MixedCollection  
12296      */
12297     fields : false,
12298     
12299     /**
12300      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12301      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12302      * @param {Roo.form.Field} field
12303      * @return {Roo.ToolbarItem}
12304      */
12305      
12306       
12307     addField : function(field) {
12308         if (!this.fields) {
12309             var autoId = 0;
12310             this.fields = new Roo.util.MixedCollection(false, function(o){
12311                 return o.id || ("item" + (++autoId));
12312             });
12313
12314         }
12315         
12316         var td = this.nextBlock();
12317         field.render(td);
12318         var ti = new Roo.Toolbar.Item(td.firstChild);
12319         ti.render(td);
12320         this.items.add(ti);
12321         this.fields.add(field);
12322         return ti;
12323     },
12324     /**
12325      * Hide the toolbar
12326      * @method hide
12327      */
12328      
12329       
12330     hide : function()
12331     {
12332         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12333         this.el.child('div').hide();
12334     },
12335     /**
12336      * Show the toolbar
12337      * @method show
12338      */
12339     show : function()
12340     {
12341         this.el.child('div').show();
12342     },
12343       
12344     // private
12345     nextBlock : function(){
12346         var td = document.createElement("td");
12347         this.tr.appendChild(td);
12348         return td;
12349     },
12350
12351     // private
12352     destroy : function(){
12353         if(this.items){ // rendered?
12354             Roo.destroy.apply(Roo, this.items.items);
12355         }
12356         if(this.fields){ // rendered?
12357             Roo.destroy.apply(Roo, this.fields.items);
12358         }
12359         Roo.Element.uncache(this.el, this.tr);
12360     }
12361 };
12362
12363 /**
12364  * @class Roo.Toolbar.Item
12365  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12366  * @constructor
12367  * Creates a new Item
12368  * @param {HTMLElement} el 
12369  */
12370 Roo.Toolbar.Item = function(el){
12371     this.el = Roo.getDom(el);
12372     this.id = Roo.id(this.el);
12373     this.hidden = false;
12374 };
12375
12376 Roo.Toolbar.Item.prototype = {
12377     
12378     /**
12379      * Get this item's HTML Element
12380      * @return {HTMLElement}
12381      */
12382     getEl : function(){
12383        return this.el;  
12384     },
12385
12386     // private
12387     render : function(td){
12388         this.td = td;
12389         td.appendChild(this.el);
12390     },
12391     
12392     /**
12393      * Removes and destroys this item.
12394      */
12395     destroy : function(){
12396         this.td.parentNode.removeChild(this.td);
12397     },
12398     
12399     /**
12400      * Shows this item.
12401      */
12402     show: function(){
12403         this.hidden = false;
12404         this.td.style.display = "";
12405     },
12406     
12407     /**
12408      * Hides this item.
12409      */
12410     hide: function(){
12411         this.hidden = true;
12412         this.td.style.display = "none";
12413     },
12414     
12415     /**
12416      * Convenience function for boolean show/hide.
12417      * @param {Boolean} visible true to show/false to hide
12418      */
12419     setVisible: function(visible){
12420         if(visible) {
12421             this.show();
12422         }else{
12423             this.hide();
12424         }
12425     },
12426     
12427     /**
12428      * Try to focus this item.
12429      */
12430     focus : function(){
12431         Roo.fly(this.el).focus();
12432     },
12433     
12434     /**
12435      * Disables this item.
12436      */
12437     disable : function(){
12438         Roo.fly(this.td).addClass("x-item-disabled");
12439         this.disabled = true;
12440         this.el.disabled = true;
12441     },
12442     
12443     /**
12444      * Enables this item.
12445      */
12446     enable : function(){
12447         Roo.fly(this.td).removeClass("x-item-disabled");
12448         this.disabled = false;
12449         this.el.disabled = false;
12450     }
12451 };
12452
12453
12454 /**
12455  * @class Roo.Toolbar.Separator
12456  * @extends Roo.Toolbar.Item
12457  * A simple toolbar separator class
12458  * @constructor
12459  * Creates a new Separator
12460  */
12461 Roo.Toolbar.Separator = function(){
12462     var s = document.createElement("span");
12463     s.className = "ytb-sep";
12464     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12465 };
12466 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12467     enable:Roo.emptyFn,
12468     disable:Roo.emptyFn,
12469     focus:Roo.emptyFn
12470 });
12471
12472 /**
12473  * @class Roo.Toolbar.Spacer
12474  * @extends Roo.Toolbar.Item
12475  * A simple element that adds extra horizontal space to a toolbar.
12476  * @constructor
12477  * Creates a new Spacer
12478  */
12479 Roo.Toolbar.Spacer = function(){
12480     var s = document.createElement("div");
12481     s.className = "ytb-spacer";
12482     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12483 };
12484 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12485     enable:Roo.emptyFn,
12486     disable:Roo.emptyFn,
12487     focus:Roo.emptyFn
12488 });
12489
12490 /**
12491  * @class Roo.Toolbar.Fill
12492  * @extends Roo.Toolbar.Spacer
12493  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12494  * @constructor
12495  * Creates a new Spacer
12496  */
12497 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12498     // private
12499     render : function(td){
12500         td.style.width = '100%';
12501         Roo.Toolbar.Fill.superclass.render.call(this, td);
12502     }
12503 });
12504
12505 /**
12506  * @class Roo.Toolbar.TextItem
12507  * @extends Roo.Toolbar.Item
12508  * A simple class that renders text directly into a toolbar.
12509  * @constructor
12510  * Creates a new TextItem
12511  * @param {String} text
12512  */
12513 Roo.Toolbar.TextItem = function(text){
12514     if (typeof(text) == 'object') {
12515         text = text.text;
12516     }
12517     var s = document.createElement("span");
12518     s.className = "ytb-text";
12519     s.innerHTML = text;
12520     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12521 };
12522 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12523     enable:Roo.emptyFn,
12524     disable:Roo.emptyFn,
12525     focus:Roo.emptyFn
12526 });
12527
12528 /**
12529  * @class Roo.Toolbar.Button
12530  * @extends Roo.Button
12531  * A button that renders into a toolbar.
12532  * @constructor
12533  * Creates a new Button
12534  * @param {Object} config A standard {@link Roo.Button} config object
12535  */
12536 Roo.Toolbar.Button = function(config){
12537     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12538 };
12539 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12540     render : function(td){
12541         this.td = td;
12542         Roo.Toolbar.Button.superclass.render.call(this, td);
12543     },
12544     
12545     /**
12546      * Removes and destroys this button
12547      */
12548     destroy : function(){
12549         Roo.Toolbar.Button.superclass.destroy.call(this);
12550         this.td.parentNode.removeChild(this.td);
12551     },
12552     
12553     /**
12554      * Shows this button
12555      */
12556     show: function(){
12557         this.hidden = false;
12558         this.td.style.display = "";
12559     },
12560     
12561     /**
12562      * Hides this button
12563      */
12564     hide: function(){
12565         this.hidden = true;
12566         this.td.style.display = "none";
12567     },
12568
12569     /**
12570      * Disables this item
12571      */
12572     disable : function(){
12573         Roo.fly(this.td).addClass("x-item-disabled");
12574         this.disabled = true;
12575     },
12576
12577     /**
12578      * Enables this item
12579      */
12580     enable : function(){
12581         Roo.fly(this.td).removeClass("x-item-disabled");
12582         this.disabled = false;
12583     }
12584 });
12585 // backwards compat
12586 Roo.ToolbarButton = Roo.Toolbar.Button;
12587
12588 /**
12589  * @class Roo.Toolbar.SplitButton
12590  * @extends Roo.SplitButton
12591  * A menu button that renders into a toolbar.
12592  * @constructor
12593  * Creates a new SplitButton
12594  * @param {Object} config A standard {@link Roo.SplitButton} config object
12595  */
12596 Roo.Toolbar.SplitButton = function(config){
12597     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12598 };
12599 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12600     render : function(td){
12601         this.td = td;
12602         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12603     },
12604     
12605     /**
12606      * Removes and destroys this button
12607      */
12608     destroy : function(){
12609         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12610         this.td.parentNode.removeChild(this.td);
12611     },
12612     
12613     /**
12614      * Shows this button
12615      */
12616     show: function(){
12617         this.hidden = false;
12618         this.td.style.display = "";
12619     },
12620     
12621     /**
12622      * Hides this button
12623      */
12624     hide: function(){
12625         this.hidden = true;
12626         this.td.style.display = "none";
12627     }
12628 });
12629
12630 // backwards compat
12631 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12632  * Based on:
12633  * Ext JS Library 1.1.1
12634  * Copyright(c) 2006-2007, Ext JS, LLC.
12635  *
12636  * Originally Released Under LGPL - original licence link has changed is not relivant.
12637  *
12638  * Fork - LGPL
12639  * <script type="text/javascript">
12640  */
12641  
12642 /**
12643  * @class Roo.PagingToolbar
12644  * @extends Roo.Toolbar
12645  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12646  * @constructor
12647  * Create a new PagingToolbar
12648  * @param {Object} config The config object
12649  */
12650 Roo.PagingToolbar = function(el, ds, config)
12651 {
12652     // old args format still supported... - xtype is prefered..
12653     if (typeof(el) == 'object' && el.xtype) {
12654         // created from xtype...
12655         config = el;
12656         ds = el.dataSource;
12657         el = config.container;
12658     }
12659     
12660     
12661     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12662     this.ds = ds;
12663     this.cursor = 0;
12664     this.renderButtons(this.el);
12665     this.bind(ds);
12666 };
12667
12668 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12669     /**
12670      * @cfg {Roo.data.Store} dataSource
12671      * The underlying data store providing the paged data
12672      */
12673     /**
12674      * @cfg {String/HTMLElement/Element} container
12675      * container The id or element that will contain the toolbar
12676      */
12677     /**
12678      * @cfg {Boolean} displayInfo
12679      * True to display the displayMsg (defaults to false)
12680      */
12681     /**
12682      * @cfg {Number} pageSize
12683      * The number of records to display per page (defaults to 20)
12684      */
12685     pageSize: 20,
12686     /**
12687      * @cfg {String} displayMsg
12688      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12689      */
12690     displayMsg : 'Displaying {0} - {1} of {2}',
12691     /**
12692      * @cfg {String} emptyMsg
12693      * The message to display when no records are found (defaults to "No data to display")
12694      */
12695     emptyMsg : 'No data to display',
12696     /**
12697      * Customizable piece of the default paging text (defaults to "Page")
12698      * @type String
12699      */
12700     beforePageText : "Page",
12701     /**
12702      * Customizable piece of the default paging text (defaults to "of %0")
12703      * @type String
12704      */
12705     afterPageText : "of {0}",
12706     /**
12707      * Customizable piece of the default paging text (defaults to "First Page")
12708      * @type String
12709      */
12710     firstText : "First Page",
12711     /**
12712      * Customizable piece of the default paging text (defaults to "Previous Page")
12713      * @type String
12714      */
12715     prevText : "Previous Page",
12716     /**
12717      * Customizable piece of the default paging text (defaults to "Next Page")
12718      * @type String
12719      */
12720     nextText : "Next Page",
12721     /**
12722      * Customizable piece of the default paging text (defaults to "Last Page")
12723      * @type String
12724      */
12725     lastText : "Last Page",
12726     /**
12727      * Customizable piece of the default paging text (defaults to "Refresh")
12728      * @type String
12729      */
12730     refreshText : "Refresh",
12731
12732     // private
12733     renderButtons : function(el){
12734         Roo.PagingToolbar.superclass.render.call(this, el);
12735         this.first = this.addButton({
12736             tooltip: this.firstText,
12737             cls: "x-btn-icon x-grid-page-first",
12738             disabled: true,
12739             handler: this.onClick.createDelegate(this, ["first"])
12740         });
12741         this.prev = this.addButton({
12742             tooltip: this.prevText,
12743             cls: "x-btn-icon x-grid-page-prev",
12744             disabled: true,
12745             handler: this.onClick.createDelegate(this, ["prev"])
12746         });
12747         this.addSeparator();
12748         this.add(this.beforePageText);
12749         this.field = Roo.get(this.addDom({
12750            tag: "input",
12751            type: "text",
12752            size: "3",
12753            value: "1",
12754            cls: "x-grid-page-number"
12755         }).el);
12756         this.field.on("keydown", this.onPagingKeydown, this);
12757         this.field.on("focus", function(){this.dom.select();});
12758         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12759         this.field.setHeight(18);
12760         this.addSeparator();
12761         this.next = this.addButton({
12762             tooltip: this.nextText,
12763             cls: "x-btn-icon x-grid-page-next",
12764             disabled: true,
12765             handler: this.onClick.createDelegate(this, ["next"])
12766         });
12767         this.last = this.addButton({
12768             tooltip: this.lastText,
12769             cls: "x-btn-icon x-grid-page-last",
12770             disabled: true,
12771             handler: this.onClick.createDelegate(this, ["last"])
12772         });
12773         this.addSeparator();
12774         this.loading = this.addButton({
12775             tooltip: this.refreshText,
12776             cls: "x-btn-icon x-grid-loading",
12777             handler: this.onClick.createDelegate(this, ["refresh"])
12778         });
12779
12780         if(this.displayInfo){
12781             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12782         }
12783     },
12784
12785     // private
12786     updateInfo : function(){
12787         if(this.displayEl){
12788             var count = this.ds.getCount();
12789             var msg = count == 0 ?
12790                 this.emptyMsg :
12791                 String.format(
12792                     this.displayMsg,
12793                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12794                 );
12795             this.displayEl.update(msg);
12796         }
12797     },
12798
12799     // private
12800     onLoad : function(ds, r, o){
12801        this.cursor = o.params ? o.params.start : 0;
12802        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12803
12804        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12805        this.field.dom.value = ap;
12806        this.first.setDisabled(ap == 1);
12807        this.prev.setDisabled(ap == 1);
12808        this.next.setDisabled(ap == ps);
12809        this.last.setDisabled(ap == ps);
12810        this.loading.enable();
12811        this.updateInfo();
12812     },
12813
12814     // private
12815     getPageData : function(){
12816         var total = this.ds.getTotalCount();
12817         return {
12818             total : total,
12819             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12820             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12821         };
12822     },
12823
12824     // private
12825     onLoadError : function(){
12826         this.loading.enable();
12827     },
12828
12829     // private
12830     onPagingKeydown : function(e){
12831         var k = e.getKey();
12832         var d = this.getPageData();
12833         if(k == e.RETURN){
12834             var v = this.field.dom.value, pageNum;
12835             if(!v || isNaN(pageNum = parseInt(v, 10))){
12836                 this.field.dom.value = d.activePage;
12837                 return;
12838             }
12839             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12840             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12841             e.stopEvent();
12842         }
12843         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))
12844         {
12845           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12846           this.field.dom.value = pageNum;
12847           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12848           e.stopEvent();
12849         }
12850         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12851         {
12852           var v = this.field.dom.value, pageNum; 
12853           var increment = (e.shiftKey) ? 10 : 1;
12854           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12855             increment *= -1;
12856           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12857             this.field.dom.value = d.activePage;
12858             return;
12859           }
12860           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12861           {
12862             this.field.dom.value = parseInt(v, 10) + increment;
12863             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12864             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12865           }
12866           e.stopEvent();
12867         }
12868     },
12869
12870     // private
12871     beforeLoad : function(){
12872         if(this.loading){
12873             this.loading.disable();
12874         }
12875     },
12876
12877     // private
12878     onClick : function(which){
12879         var ds = this.ds;
12880         switch(which){
12881             case "first":
12882                 ds.load({params:{start: 0, limit: this.pageSize}});
12883             break;
12884             case "prev":
12885                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12886             break;
12887             case "next":
12888                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12889             break;
12890             case "last":
12891                 var total = ds.getTotalCount();
12892                 var extra = total % this.pageSize;
12893                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12894                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12895             break;
12896             case "refresh":
12897                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12898             break;
12899         }
12900     },
12901
12902     /**
12903      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12904      * @param {Roo.data.Store} store The data store to unbind
12905      */
12906     unbind : function(ds){
12907         ds.un("beforeload", this.beforeLoad, this);
12908         ds.un("load", this.onLoad, this);
12909         ds.un("loadexception", this.onLoadError, this);
12910         ds.un("remove", this.updateInfo, this);
12911         ds.un("add", this.updateInfo, this);
12912         this.ds = undefined;
12913     },
12914
12915     /**
12916      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12917      * @param {Roo.data.Store} store The data store to bind
12918      */
12919     bind : function(ds){
12920         ds.on("beforeload", this.beforeLoad, this);
12921         ds.on("load", this.onLoad, this);
12922         ds.on("loadexception", this.onLoadError, this);
12923         ds.on("remove", this.updateInfo, this);
12924         ds.on("add", this.updateInfo, this);
12925         this.ds = ds;
12926     }
12927 });/*
12928  * Based on:
12929  * Ext JS Library 1.1.1
12930  * Copyright(c) 2006-2007, Ext JS, LLC.
12931  *
12932  * Originally Released Under LGPL - original licence link has changed is not relivant.
12933  *
12934  * Fork - LGPL
12935  * <script type="text/javascript">
12936  */
12937
12938 /**
12939  * @class Roo.Resizable
12940  * @extends Roo.util.Observable
12941  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12942  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12943  * 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
12944  * the element will be wrapped for you automatically.</p>
12945  * <p>Here is the list of valid resize handles:</p>
12946  * <pre>
12947 Value   Description
12948 ------  -------------------
12949  'n'     north
12950  's'     south
12951  'e'     east
12952  'w'     west
12953  'nw'    northwest
12954  'sw'    southwest
12955  'se'    southeast
12956  'ne'    northeast
12957  'all'   all
12958 </pre>
12959  * <p>Here's an example showing the creation of a typical Resizable:</p>
12960  * <pre><code>
12961 var resizer = new Roo.Resizable("element-id", {
12962     handles: 'all',
12963     minWidth: 200,
12964     minHeight: 100,
12965     maxWidth: 500,
12966     maxHeight: 400,
12967     pinned: true
12968 });
12969 resizer.on("resize", myHandler);
12970 </code></pre>
12971  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12972  * resizer.east.setDisplayed(false);</p>
12973  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12974  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12975  * resize operation's new size (defaults to [0, 0])
12976  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12977  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12978  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12979  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12980  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12981  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12982  * @cfg {Number} width The width of the element in pixels (defaults to null)
12983  * @cfg {Number} height The height of the element in pixels (defaults to null)
12984  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12985  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12986  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12987  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12988  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12989  * in favor of the handles config option (defaults to false)
12990  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12991  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12992  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12993  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12994  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12995  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12996  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12997  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12998  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12999  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13000  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13001  * @constructor
13002  * Create a new resizable component
13003  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13004  * @param {Object} config configuration options
13005   */
13006 Roo.Resizable = function(el, config){
13007     this.el = Roo.get(el);
13008
13009     if(config && config.wrap){
13010         config.resizeChild = this.el;
13011         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13012         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13013         this.el.setStyle("overflow", "hidden");
13014         this.el.setPositioning(config.resizeChild.getPositioning());
13015         config.resizeChild.clearPositioning();
13016         if(!config.width || !config.height){
13017             var csize = config.resizeChild.getSize();
13018             this.el.setSize(csize.width, csize.height);
13019         }
13020         if(config.pinned && !config.adjustments){
13021             config.adjustments = "auto";
13022         }
13023     }
13024
13025     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13026     this.proxy.unselectable();
13027     this.proxy.enableDisplayMode('block');
13028
13029     Roo.apply(this, config);
13030
13031     if(this.pinned){
13032         this.disableTrackOver = true;
13033         this.el.addClass("x-resizable-pinned");
13034     }
13035     // if the element isn't positioned, make it relative
13036     var position = this.el.getStyle("position");
13037     if(position != "absolute" && position != "fixed"){
13038         this.el.setStyle("position", "relative");
13039     }
13040     if(!this.handles){ // no handles passed, must be legacy style
13041         this.handles = 's,e,se';
13042         if(this.multiDirectional){
13043             this.handles += ',n,w';
13044         }
13045     }
13046     if(this.handles == "all"){
13047         this.handles = "n s e w ne nw se sw";
13048     }
13049     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13050     var ps = Roo.Resizable.positions;
13051     for(var i = 0, len = hs.length; i < len; i++){
13052         if(hs[i] && ps[hs[i]]){
13053             var pos = ps[hs[i]];
13054             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13055         }
13056     }
13057     // legacy
13058     this.corner = this.southeast;
13059
13060     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
13061         this.updateBox = true;
13062     }
13063
13064     this.activeHandle = null;
13065
13066     if(this.resizeChild){
13067         if(typeof this.resizeChild == "boolean"){
13068             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13069         }else{
13070             this.resizeChild = Roo.get(this.resizeChild, true);
13071         }
13072     }
13073
13074     if(this.adjustments == "auto"){
13075         var rc = this.resizeChild;
13076         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13077         if(rc && (hw || hn)){
13078             rc.position("relative");
13079             rc.setLeft(hw ? hw.el.getWidth() : 0);
13080             rc.setTop(hn ? hn.el.getHeight() : 0);
13081         }
13082         this.adjustments = [
13083             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13084             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13085         ];
13086     }
13087
13088     if(this.draggable){
13089         this.dd = this.dynamic ?
13090             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13091         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13092     }
13093
13094     // public events
13095     this.addEvents({
13096         /**
13097          * @event beforeresize
13098          * Fired before resize is allowed. Set enabled to false to cancel resize.
13099          * @param {Roo.Resizable} this
13100          * @param {Roo.EventObject} e The mousedown event
13101          */
13102         "beforeresize" : true,
13103         /**
13104          * @event resize
13105          * Fired after a resize.
13106          * @param {Roo.Resizable} this
13107          * @param {Number} width The new width
13108          * @param {Number} height The new height
13109          * @param {Roo.EventObject} e The mouseup event
13110          */
13111         "resize" : true
13112     });
13113
13114     if(this.width !== null && this.height !== null){
13115         this.resizeTo(this.width, this.height);
13116     }else{
13117         this.updateChildSize();
13118     }
13119     if(Roo.isIE){
13120         this.el.dom.style.zoom = 1;
13121     }
13122     Roo.Resizable.superclass.constructor.call(this);
13123 };
13124
13125 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13126         resizeChild : false,
13127         adjustments : [0, 0],
13128         minWidth : 5,
13129         minHeight : 5,
13130         maxWidth : 10000,
13131         maxHeight : 10000,
13132         enabled : true,
13133         animate : false,
13134         duration : .35,
13135         dynamic : false,
13136         handles : false,
13137         multiDirectional : false,
13138         disableTrackOver : false,
13139         easing : 'easeOutStrong',
13140         widthIncrement : 0,
13141         heightIncrement : 0,
13142         pinned : false,
13143         width : null,
13144         height : null,
13145         preserveRatio : false,
13146         transparent: false,
13147         minX: 0,
13148         minY: 0,
13149         draggable: false,
13150
13151         /**
13152          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13153          */
13154         constrainTo: undefined,
13155         /**
13156          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13157          */
13158         resizeRegion: undefined,
13159
13160
13161     /**
13162      * Perform a manual resize
13163      * @param {Number} width
13164      * @param {Number} height
13165      */
13166     resizeTo : function(width, height){
13167         this.el.setSize(width, height);
13168         this.updateChildSize();
13169         this.fireEvent("resize", this, width, height, null);
13170     },
13171
13172     // private
13173     startSizing : function(e, handle){
13174         this.fireEvent("beforeresize", this, e);
13175         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13176
13177             if(!this.overlay){
13178                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13179                 this.overlay.unselectable();
13180                 this.overlay.enableDisplayMode("block");
13181                 this.overlay.on("mousemove", this.onMouseMove, this);
13182                 this.overlay.on("mouseup", this.onMouseUp, this);
13183             }
13184             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13185
13186             this.resizing = true;
13187             this.startBox = this.el.getBox();
13188             this.startPoint = e.getXY();
13189             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13190                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13191
13192             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13193             this.overlay.show();
13194
13195             if(this.constrainTo) {
13196                 var ct = Roo.get(this.constrainTo);
13197                 this.resizeRegion = ct.getRegion().adjust(
13198                     ct.getFrameWidth('t'),
13199                     ct.getFrameWidth('l'),
13200                     -ct.getFrameWidth('b'),
13201                     -ct.getFrameWidth('r')
13202                 );
13203             }
13204
13205             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13206             this.proxy.show();
13207             this.proxy.setBox(this.startBox);
13208             if(!this.dynamic){
13209                 this.proxy.setStyle('visibility', 'visible');
13210             }
13211         }
13212     },
13213
13214     // private
13215     onMouseDown : function(handle, e){
13216         if(this.enabled){
13217             e.stopEvent();
13218             this.activeHandle = handle;
13219             this.startSizing(e, handle);
13220         }
13221     },
13222
13223     // private
13224     onMouseUp : function(e){
13225         var size = this.resizeElement();
13226         this.resizing = false;
13227         this.handleOut();
13228         this.overlay.hide();
13229         this.proxy.hide();
13230         this.fireEvent("resize", this, size.width, size.height, e);
13231     },
13232
13233     // private
13234     updateChildSize : function(){
13235         if(this.resizeChild){
13236             var el = this.el;
13237             var child = this.resizeChild;
13238             var adj = this.adjustments;
13239             if(el.dom.offsetWidth){
13240                 var b = el.getSize(true);
13241                 child.setSize(b.width+adj[0], b.height+adj[1]);
13242             }
13243             // Second call here for IE
13244             // The first call enables instant resizing and
13245             // the second call corrects scroll bars if they
13246             // exist
13247             if(Roo.isIE){
13248                 setTimeout(function(){
13249                     if(el.dom.offsetWidth){
13250                         var b = el.getSize(true);
13251                         child.setSize(b.width+adj[0], b.height+adj[1]);
13252                     }
13253                 }, 10);
13254             }
13255         }
13256     },
13257
13258     // private
13259     snap : function(value, inc, min){
13260         if(!inc || !value) return value;
13261         var newValue = value;
13262         var m = value % inc;
13263         if(m > 0){
13264             if(m > (inc/2)){
13265                 newValue = value + (inc-m);
13266             }else{
13267                 newValue = value - m;
13268             }
13269         }
13270         return Math.max(min, newValue);
13271     },
13272
13273     // private
13274     resizeElement : function(){
13275         var box = this.proxy.getBox();
13276         if(this.updateBox){
13277             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13278         }else{
13279             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13280         }
13281         this.updateChildSize();
13282         if(!this.dynamic){
13283             this.proxy.hide();
13284         }
13285         return box;
13286     },
13287
13288     // private
13289     constrain : function(v, diff, m, mx){
13290         if(v - diff < m){
13291             diff = v - m;
13292         }else if(v - diff > mx){
13293             diff = mx - v;
13294         }
13295         return diff;
13296     },
13297
13298     // private
13299     onMouseMove : function(e){
13300         if(this.enabled){
13301             try{// try catch so if something goes wrong the user doesn't get hung
13302
13303             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13304                 return;
13305             }
13306
13307             //var curXY = this.startPoint;
13308             var curSize = this.curSize || this.startBox;
13309             var x = this.startBox.x, y = this.startBox.y;
13310             var ox = x, oy = y;
13311             var w = curSize.width, h = curSize.height;
13312             var ow = w, oh = h;
13313             var mw = this.minWidth, mh = this.minHeight;
13314             var mxw = this.maxWidth, mxh = this.maxHeight;
13315             var wi = this.widthIncrement;
13316             var hi = this.heightIncrement;
13317
13318             var eventXY = e.getXY();
13319             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13320             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13321
13322             var pos = this.activeHandle.position;
13323
13324             switch(pos){
13325                 case "east":
13326                     w += diffX;
13327                     w = Math.min(Math.max(mw, w), mxw);
13328                     break;
13329                 case "south":
13330                     h += diffY;
13331                     h = Math.min(Math.max(mh, h), mxh);
13332                     break;
13333                 case "southeast":
13334                     w += diffX;
13335                     h += diffY;
13336                     w = Math.min(Math.max(mw, w), mxw);
13337                     h = Math.min(Math.max(mh, h), mxh);
13338                     break;
13339                 case "north":
13340                     diffY = this.constrain(h, diffY, mh, mxh);
13341                     y += diffY;
13342                     h -= diffY;
13343                     break;
13344                 case "west":
13345                     diffX = this.constrain(w, diffX, mw, mxw);
13346                     x += diffX;
13347                     w -= diffX;
13348                     break;
13349                 case "northeast":
13350                     w += diffX;
13351                     w = Math.min(Math.max(mw, w), mxw);
13352                     diffY = this.constrain(h, diffY, mh, mxh);
13353                     y += diffY;
13354                     h -= diffY;
13355                     break;
13356                 case "northwest":
13357                     diffX = this.constrain(w, diffX, mw, mxw);
13358                     diffY = this.constrain(h, diffY, mh, mxh);
13359                     y += diffY;
13360                     h -= diffY;
13361                     x += diffX;
13362                     w -= diffX;
13363                     break;
13364                case "southwest":
13365                     diffX = this.constrain(w, diffX, mw, mxw);
13366                     h += diffY;
13367                     h = Math.min(Math.max(mh, h), mxh);
13368                     x += diffX;
13369                     w -= diffX;
13370                     break;
13371             }
13372
13373             var sw = this.snap(w, wi, mw);
13374             var sh = this.snap(h, hi, mh);
13375             if(sw != w || sh != h){
13376                 switch(pos){
13377                     case "northeast":
13378                         y -= sh - h;
13379                     break;
13380                     case "north":
13381                         y -= sh - h;
13382                         break;
13383                     case "southwest":
13384                         x -= sw - w;
13385                     break;
13386                     case "west":
13387                         x -= sw - w;
13388                         break;
13389                     case "northwest":
13390                         x -= sw - w;
13391                         y -= sh - h;
13392                     break;
13393                 }
13394                 w = sw;
13395                 h = sh;
13396             }
13397
13398             if(this.preserveRatio){
13399                 switch(pos){
13400                     case "southeast":
13401                     case "east":
13402                         h = oh * (w/ow);
13403                         h = Math.min(Math.max(mh, h), mxh);
13404                         w = ow * (h/oh);
13405                        break;
13406                     case "south":
13407                         w = ow * (h/oh);
13408                         w = Math.min(Math.max(mw, w), mxw);
13409                         h = oh * (w/ow);
13410                         break;
13411                     case "northeast":
13412                         w = ow * (h/oh);
13413                         w = Math.min(Math.max(mw, w), mxw);
13414                         h = oh * (w/ow);
13415                     break;
13416                     case "north":
13417                         var tw = w;
13418                         w = ow * (h/oh);
13419                         w = Math.min(Math.max(mw, w), mxw);
13420                         h = oh * (w/ow);
13421                         x += (tw - w) / 2;
13422                         break;
13423                     case "southwest":
13424                         h = oh * (w/ow);
13425                         h = Math.min(Math.max(mh, h), mxh);
13426                         var tw = w;
13427                         w = ow * (h/oh);
13428                         x += tw - w;
13429                         break;
13430                     case "west":
13431                         var th = h;
13432                         h = oh * (w/ow);
13433                         h = Math.min(Math.max(mh, h), mxh);
13434                         y += (th - h) / 2;
13435                         var tw = w;
13436                         w = ow * (h/oh);
13437                         x += tw - w;
13438                        break;
13439                     case "northwest":
13440                         var tw = w;
13441                         var th = h;
13442                         h = oh * (w/ow);
13443                         h = Math.min(Math.max(mh, h), mxh);
13444                         w = ow * (h/oh);
13445                         y += th - h;
13446                          x += tw - w;
13447                        break;
13448
13449                 }
13450             }
13451             this.proxy.setBounds(x, y, w, h);
13452             if(this.dynamic){
13453                 this.resizeElement();
13454             }
13455             }catch(e){}
13456         }
13457     },
13458
13459     // private
13460     handleOver : function(){
13461         if(this.enabled){
13462             this.el.addClass("x-resizable-over");
13463         }
13464     },
13465
13466     // private
13467     handleOut : function(){
13468         if(!this.resizing){
13469             this.el.removeClass("x-resizable-over");
13470         }
13471     },
13472
13473     /**
13474      * Returns the element this component is bound to.
13475      * @return {Roo.Element}
13476      */
13477     getEl : function(){
13478         return this.el;
13479     },
13480
13481     /**
13482      * Returns the resizeChild element (or null).
13483      * @return {Roo.Element}
13484      */
13485     getResizeChild : function(){
13486         return this.resizeChild;
13487     },
13488
13489     /**
13490      * Destroys this resizable. If the element was wrapped and
13491      * removeEl is not true then the element remains.
13492      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13493      */
13494     destroy : function(removeEl){
13495         this.proxy.remove();
13496         if(this.overlay){
13497             this.overlay.removeAllListeners();
13498             this.overlay.remove();
13499         }
13500         var ps = Roo.Resizable.positions;
13501         for(var k in ps){
13502             if(typeof ps[k] != "function" && this[ps[k]]){
13503                 var h = this[ps[k]];
13504                 h.el.removeAllListeners();
13505                 h.el.remove();
13506             }
13507         }
13508         if(removeEl){
13509             this.el.update("");
13510             this.el.remove();
13511         }
13512     }
13513 });
13514
13515 // private
13516 // hash to map config positions to true positions
13517 Roo.Resizable.positions = {
13518     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
13519 };
13520
13521 // private
13522 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13523     if(!this.tpl){
13524         // only initialize the template if resizable is used
13525         var tpl = Roo.DomHelper.createTemplate(
13526             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13527         );
13528         tpl.compile();
13529         Roo.Resizable.Handle.prototype.tpl = tpl;
13530     }
13531     this.position = pos;
13532     this.rz = rz;
13533     this.el = this.tpl.append(rz.el.dom, [this.position], true);
13534     this.el.unselectable();
13535     if(transparent){
13536         this.el.setOpacity(0);
13537     }
13538     this.el.on("mousedown", this.onMouseDown, this);
13539     if(!disableTrackOver){
13540         this.el.on("mouseover", this.onMouseOver, this);
13541         this.el.on("mouseout", this.onMouseOut, this);
13542     }
13543 };
13544
13545 // private
13546 Roo.Resizable.Handle.prototype = {
13547     afterResize : function(rz){
13548         // do nothing
13549     },
13550     // private
13551     onMouseDown : function(e){
13552         this.rz.onMouseDown(this, e);
13553     },
13554     // private
13555     onMouseOver : function(e){
13556         this.rz.handleOver(this, e);
13557     },
13558     // private
13559     onMouseOut : function(e){
13560         this.rz.handleOut(this, e);
13561     }
13562 };/*
13563  * Based on:
13564  * Ext JS Library 1.1.1
13565  * Copyright(c) 2006-2007, Ext JS, LLC.
13566  *
13567  * Originally Released Under LGPL - original licence link has changed is not relivant.
13568  *
13569  * Fork - LGPL
13570  * <script type="text/javascript">
13571  */
13572
13573 /**
13574  * @class Roo.Editor
13575  * @extends Roo.Component
13576  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13577  * @constructor
13578  * Create a new Editor
13579  * @param {Roo.form.Field} field The Field object (or descendant)
13580  * @param {Object} config The config object
13581  */
13582 Roo.Editor = function(field, config){
13583     Roo.Editor.superclass.constructor.call(this, config);
13584     this.field = field;
13585     this.addEvents({
13586         /**
13587              * @event beforestartedit
13588              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13589              * false from the handler of this event.
13590              * @param {Editor} this
13591              * @param {Roo.Element} boundEl The underlying element bound to this editor
13592              * @param {Mixed} value The field value being set
13593              */
13594         "beforestartedit" : true,
13595         /**
13596              * @event startedit
13597              * Fires when this editor is displayed
13598              * @param {Roo.Element} boundEl The underlying element bound to this editor
13599              * @param {Mixed} value The starting field value
13600              */
13601         "startedit" : true,
13602         /**
13603              * @event beforecomplete
13604              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13605              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13606              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13607              * event will not fire since no edit actually occurred.
13608              * @param {Editor} this
13609              * @param {Mixed} value The current field value
13610              * @param {Mixed} startValue The original field value
13611              */
13612         "beforecomplete" : true,
13613         /**
13614              * @event complete
13615              * Fires after editing is complete and any changed value has been written to the underlying field.
13616              * @param {Editor} this
13617              * @param {Mixed} value The current field value
13618              * @param {Mixed} startValue The original field value
13619              */
13620         "complete" : true,
13621         /**
13622          * @event specialkey
13623          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13624          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13625          * @param {Roo.form.Field} this
13626          * @param {Roo.EventObject} e The event object
13627          */
13628         "specialkey" : true
13629     });
13630 };
13631
13632 Roo.extend(Roo.Editor, Roo.Component, {
13633     /**
13634      * @cfg {Boolean/String} autosize
13635      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13636      * or "height" to adopt the height only (defaults to false)
13637      */
13638     /**
13639      * @cfg {Boolean} revertInvalid
13640      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13641      * validation fails (defaults to true)
13642      */
13643     /**
13644      * @cfg {Boolean} ignoreNoChange
13645      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13646      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13647      * will never be ignored.
13648      */
13649     /**
13650      * @cfg {Boolean} hideEl
13651      * False to keep the bound element visible while the editor is displayed (defaults to true)
13652      */
13653     /**
13654      * @cfg {Mixed} value
13655      * The data value of the underlying field (defaults to "")
13656      */
13657     value : "",
13658     /**
13659      * @cfg {String} alignment
13660      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13661      */
13662     alignment: "c-c?",
13663     /**
13664      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13665      * for bottom-right shadow (defaults to "frame")
13666      */
13667     shadow : "frame",
13668     /**
13669      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13670      */
13671     constrain : false,
13672     /**
13673      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13674      */
13675     completeOnEnter : false,
13676     /**
13677      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13678      */
13679     cancelOnEsc : false,
13680     /**
13681      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13682      */
13683     updateEl : false,
13684
13685     // private
13686     onRender : function(ct, position){
13687         this.el = new Roo.Layer({
13688             shadow: this.shadow,
13689             cls: "x-editor",
13690             parentEl : ct,
13691             shim : this.shim,
13692             shadowOffset:4,
13693             id: this.id,
13694             constrain: this.constrain
13695         });
13696         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13697         if(this.field.msgTarget != 'title'){
13698             this.field.msgTarget = 'qtip';
13699         }
13700         this.field.render(this.el);
13701         if(Roo.isGecko){
13702             this.field.el.dom.setAttribute('autocomplete', 'off');
13703         }
13704         this.field.on("specialkey", this.onSpecialKey, this);
13705         if(this.swallowKeys){
13706             this.field.el.swallowEvent(['keydown','keypress']);
13707         }
13708         this.field.show();
13709         this.field.on("blur", this.onBlur, this);
13710         if(this.field.grow){
13711             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13712         }
13713     },
13714
13715     onSpecialKey : function(field, e){
13716         if(this.completeOnEnter && e.getKey() == e.ENTER){
13717             e.stopEvent();
13718             this.completeEdit();
13719         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13720             this.cancelEdit();
13721         }else{
13722             this.fireEvent('specialkey', field, e);
13723         }
13724     },
13725
13726     /**
13727      * Starts the editing process and shows the editor.
13728      * @param {String/HTMLElement/Element} el The element to edit
13729      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13730       * to the innerHTML of el.
13731      */
13732     startEdit : function(el, value){
13733         if(this.editing){
13734             this.completeEdit();
13735         }
13736         this.boundEl = Roo.get(el);
13737         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13738         if(!this.rendered){
13739             this.render(this.parentEl || document.body);
13740         }
13741         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13742             return;
13743         }
13744         this.startValue = v;
13745         this.field.setValue(v);
13746         if(this.autoSize){
13747             var sz = this.boundEl.getSize();
13748             switch(this.autoSize){
13749                 case "width":
13750                 this.setSize(sz.width,  "");
13751                 break;
13752                 case "height":
13753                 this.setSize("",  sz.height);
13754                 break;
13755                 default:
13756                 this.setSize(sz.width,  sz.height);
13757             }
13758         }
13759         this.el.alignTo(this.boundEl, this.alignment);
13760         this.editing = true;
13761         if(Roo.QuickTips){
13762             Roo.QuickTips.disable();
13763         }
13764         this.show();
13765     },
13766
13767     /**
13768      * Sets the height and width of this editor.
13769      * @param {Number} width The new width
13770      * @param {Number} height The new height
13771      */
13772     setSize : function(w, h){
13773         this.field.setSize(w, h);
13774         if(this.el){
13775             this.el.sync();
13776         }
13777     },
13778
13779     /**
13780      * Realigns the editor to the bound field based on the current alignment config value.
13781      */
13782     realign : function(){
13783         this.el.alignTo(this.boundEl, this.alignment);
13784     },
13785
13786     /**
13787      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13788      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13789      */
13790     completeEdit : function(remainVisible){
13791         if(!this.editing){
13792             return;
13793         }
13794         var v = this.getValue();
13795         if(this.revertInvalid !== false && !this.field.isValid()){
13796             v = this.startValue;
13797             this.cancelEdit(true);
13798         }
13799         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13800             this.editing = false;
13801             this.hide();
13802             return;
13803         }
13804         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13805             this.editing = false;
13806             if(this.updateEl && this.boundEl){
13807                 this.boundEl.update(v);
13808             }
13809             if(remainVisible !== true){
13810                 this.hide();
13811             }
13812             this.fireEvent("complete", this, v, this.startValue);
13813         }
13814     },
13815
13816     // private
13817     onShow : function(){
13818         this.el.show();
13819         if(this.hideEl !== false){
13820             this.boundEl.hide();
13821         }
13822         this.field.show();
13823         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13824             this.fixIEFocus = true;
13825             this.deferredFocus.defer(50, this);
13826         }else{
13827             this.field.focus();
13828         }
13829         this.fireEvent("startedit", this.boundEl, this.startValue);
13830     },
13831
13832     deferredFocus : function(){
13833         if(this.editing){
13834             this.field.focus();
13835         }
13836     },
13837
13838     /**
13839      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13840      * reverted to the original starting value.
13841      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13842      * cancel (defaults to false)
13843      */
13844     cancelEdit : function(remainVisible){
13845         if(this.editing){
13846             this.setValue(this.startValue);
13847             if(remainVisible !== true){
13848                 this.hide();
13849             }
13850         }
13851     },
13852
13853     // private
13854     onBlur : function(){
13855         if(this.allowBlur !== true && this.editing){
13856             this.completeEdit();
13857         }
13858     },
13859
13860     // private
13861     onHide : function(){
13862         if(this.editing){
13863             this.completeEdit();
13864             return;
13865         }
13866         this.field.blur();
13867         if(this.field.collapse){
13868             this.field.collapse();
13869         }
13870         this.el.hide();
13871         if(this.hideEl !== false){
13872             this.boundEl.show();
13873         }
13874         if(Roo.QuickTips){
13875             Roo.QuickTips.enable();
13876         }
13877     },
13878
13879     /**
13880      * Sets the data value of the editor
13881      * @param {Mixed} value Any valid value supported by the underlying field
13882      */
13883     setValue : function(v){
13884         this.field.setValue(v);
13885     },
13886
13887     /**
13888      * Gets the data value of the editor
13889      * @return {Mixed} The data value
13890      */
13891     getValue : function(){
13892         return this.field.getValue();
13893     }
13894 });/*
13895  * Based on:
13896  * Ext JS Library 1.1.1
13897  * Copyright(c) 2006-2007, Ext JS, LLC.
13898  *
13899  * Originally Released Under LGPL - original licence link has changed is not relivant.
13900  *
13901  * Fork - LGPL
13902  * <script type="text/javascript">
13903  */
13904  
13905 /**
13906  * @class Roo.BasicDialog
13907  * @extends Roo.util.Observable
13908  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13909  * <pre><code>
13910 var dlg = new Roo.BasicDialog("my-dlg", {
13911     height: 200,
13912     width: 300,
13913     minHeight: 100,
13914     minWidth: 150,
13915     modal: true,
13916     proxyDrag: true,
13917     shadow: true
13918 });
13919 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13920 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13921 dlg.addButton('Cancel', dlg.hide, dlg);
13922 dlg.show();
13923 </code></pre>
13924   <b>A Dialog should always be a direct child of the body element.</b>
13925  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13926  * @cfg {String} title Default text to display in the title bar (defaults to null)
13927  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13928  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13929  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13930  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13931  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13932  * (defaults to null with no animation)
13933  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13934  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13935  * property for valid values (defaults to 'all')
13936  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13937  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13938  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13939  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13940  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13941  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13942  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13943  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13944  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13945  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13946  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13947  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13948  * draggable = true (defaults to false)
13949  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13950  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13951  * shadow (defaults to false)
13952  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13953  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13954  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13955  * @cfg {Array} buttons Array of buttons
13956  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13957  * @constructor
13958  * Create a new BasicDialog.
13959  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13960  * @param {Object} config Configuration options
13961  */
13962 Roo.BasicDialog = function(el, config){
13963     this.el = Roo.get(el);
13964     var dh = Roo.DomHelper;
13965     if(!this.el && config && config.autoCreate){
13966         if(typeof config.autoCreate == "object"){
13967             if(!config.autoCreate.id){
13968                 config.autoCreate.id = el;
13969             }
13970             this.el = dh.append(document.body,
13971                         config.autoCreate, true);
13972         }else{
13973             this.el = dh.append(document.body,
13974                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13975         }
13976     }
13977     el = this.el;
13978     el.setDisplayed(true);
13979     el.hide = this.hideAction;
13980     this.id = el.id;
13981     el.addClass("x-dlg");
13982
13983     Roo.apply(this, config);
13984
13985     this.proxy = el.createProxy("x-dlg-proxy");
13986     this.proxy.hide = this.hideAction;
13987     this.proxy.setOpacity(.5);
13988     this.proxy.hide();
13989
13990     if(config.width){
13991         el.setWidth(config.width);
13992     }
13993     if(config.height){
13994         el.setHeight(config.height);
13995     }
13996     this.size = el.getSize();
13997     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13998         this.xy = [config.x,config.y];
13999     }else{
14000         this.xy = el.getCenterXY(true);
14001     }
14002     /** The header element @type Roo.Element */
14003     this.header = el.child("> .x-dlg-hd");
14004     /** The body element @type Roo.Element */
14005     this.body = el.child("> .x-dlg-bd");
14006     /** The footer element @type Roo.Element */
14007     this.footer = el.child("> .x-dlg-ft");
14008
14009     if(!this.header){
14010         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14011     }
14012     if(!this.body){
14013         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14014     }
14015
14016     this.header.unselectable();
14017     if(this.title){
14018         this.header.update(this.title);
14019     }
14020     // this element allows the dialog to be focused for keyboard event
14021     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14022     this.focusEl.swallowEvent("click", true);
14023
14024     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14025
14026     // wrap the body and footer for special rendering
14027     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14028     if(this.footer){
14029         this.bwrap.dom.appendChild(this.footer.dom);
14030     }
14031
14032     this.bg = this.el.createChild({
14033         tag: "div", cls:"x-dlg-bg",
14034         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14035     });
14036     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14037
14038
14039     if(this.autoScroll !== false && !this.autoTabs){
14040         this.body.setStyle("overflow", "auto");
14041     }
14042
14043     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14044
14045     if(this.closable !== false){
14046         this.el.addClass("x-dlg-closable");
14047         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14048         this.close.on("click", this.closeClick, this);
14049         this.close.addClassOnOver("x-dlg-close-over");
14050     }
14051     if(this.collapsible !== false){
14052         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14053         this.collapseBtn.on("click", this.collapseClick, this);
14054         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14055         this.header.on("dblclick", this.collapseClick, this);
14056     }
14057     if(this.resizable !== false){
14058         this.el.addClass("x-dlg-resizable");
14059         this.resizer = new Roo.Resizable(el, {
14060             minWidth: this.minWidth || 80,
14061             minHeight:this.minHeight || 80,
14062             handles: this.resizeHandles || "all",
14063             pinned: true
14064         });
14065         this.resizer.on("beforeresize", this.beforeResize, this);
14066         this.resizer.on("resize", this.onResize, this);
14067     }
14068     if(this.draggable !== false){
14069         el.addClass("x-dlg-draggable");
14070         if (!this.proxyDrag) {
14071             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14072         }
14073         else {
14074             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14075         }
14076         dd.setHandleElId(this.header.id);
14077         dd.endDrag = this.endMove.createDelegate(this);
14078         dd.startDrag = this.startMove.createDelegate(this);
14079         dd.onDrag = this.onDrag.createDelegate(this);
14080         dd.scroll = false;
14081         this.dd = dd;
14082     }
14083     if(this.modal){
14084         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14085         this.mask.enableDisplayMode("block");
14086         this.mask.hide();
14087         this.el.addClass("x-dlg-modal");
14088     }
14089     if(this.shadow){
14090         this.shadow = new Roo.Shadow({
14091             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14092             offset : this.shadowOffset
14093         });
14094     }else{
14095         this.shadowOffset = 0;
14096     }
14097     if(Roo.useShims && this.shim !== false){
14098         this.shim = this.el.createShim();
14099         this.shim.hide = this.hideAction;
14100         this.shim.hide();
14101     }else{
14102         this.shim = false;
14103     }
14104     if(this.autoTabs){
14105         this.initTabs();
14106     }
14107     if (this.buttons) { 
14108         var bts= this.buttons;
14109         this.buttons = [];
14110         Roo.each(bts, function(b) {
14111             this.addButton(b);
14112         }, this);
14113     }
14114     
14115     
14116     this.addEvents({
14117         /**
14118          * @event keydown
14119          * Fires when a key is pressed
14120          * @param {Roo.BasicDialog} this
14121          * @param {Roo.EventObject} e
14122          */
14123         "keydown" : true,
14124         /**
14125          * @event move
14126          * Fires when this dialog is moved by the user.
14127          * @param {Roo.BasicDialog} this
14128          * @param {Number} x The new page X
14129          * @param {Number} y The new page Y
14130          */
14131         "move" : true,
14132         /**
14133          * @event resize
14134          * Fires when this dialog is resized by the user.
14135          * @param {Roo.BasicDialog} this
14136          * @param {Number} width The new width
14137          * @param {Number} height The new height
14138          */
14139         "resize" : true,
14140         /**
14141          * @event beforehide
14142          * Fires before this dialog is hidden.
14143          * @param {Roo.BasicDialog} this
14144          */
14145         "beforehide" : true,
14146         /**
14147          * @event hide
14148          * Fires when this dialog is hidden.
14149          * @param {Roo.BasicDialog} this
14150          */
14151         "hide" : true,
14152         /**
14153          * @event beforeshow
14154          * Fires before this dialog is shown.
14155          * @param {Roo.BasicDialog} this
14156          */
14157         "beforeshow" : true,
14158         /**
14159          * @event show
14160          * Fires when this dialog is shown.
14161          * @param {Roo.BasicDialog} this
14162          */
14163         "show" : true
14164     });
14165     el.on("keydown", this.onKeyDown, this);
14166     el.on("mousedown", this.toFront, this);
14167     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14168     this.el.hide();
14169     Roo.DialogManager.register(this);
14170     Roo.BasicDialog.superclass.constructor.call(this);
14171 };
14172
14173 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14174     shadowOffset: Roo.isIE ? 6 : 5,
14175     minHeight: 80,
14176     minWidth: 200,
14177     minButtonWidth: 75,
14178     defaultButton: null,
14179     buttonAlign: "right",
14180     tabTag: 'div',
14181     firstShow: true,
14182
14183     /**
14184      * Sets the dialog title text
14185      * @param {String} text The title text to display
14186      * @return {Roo.BasicDialog} this
14187      */
14188     setTitle : function(text){
14189         this.header.update(text);
14190         return this;
14191     },
14192
14193     // private
14194     closeClick : function(){
14195         this.hide();
14196     },
14197
14198     // private
14199     collapseClick : function(){
14200         this[this.collapsed ? "expand" : "collapse"]();
14201     },
14202
14203     /**
14204      * Collapses the dialog to its minimized state (only the title bar is visible).
14205      * Equivalent to the user clicking the collapse dialog button.
14206      */
14207     collapse : function(){
14208         if(!this.collapsed){
14209             this.collapsed = true;
14210             this.el.addClass("x-dlg-collapsed");
14211             this.restoreHeight = this.el.getHeight();
14212             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14213         }
14214     },
14215
14216     /**
14217      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14218      * clicking the expand dialog button.
14219      */
14220     expand : function(){
14221         if(this.collapsed){
14222             this.collapsed = false;
14223             this.el.removeClass("x-dlg-collapsed");
14224             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14225         }
14226     },
14227
14228     /**
14229      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14230      * @return {Roo.TabPanel} The tabs component
14231      */
14232     initTabs : function(){
14233         var tabs = this.getTabs();
14234         while(tabs.getTab(0)){
14235             tabs.removeTab(0);
14236         }
14237         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14238             var dom = el.dom;
14239             tabs.addTab(Roo.id(dom), dom.title);
14240             dom.title = "";
14241         });
14242         tabs.activate(0);
14243         return tabs;
14244     },
14245
14246     // private
14247     beforeResize : function(){
14248         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14249     },
14250
14251     // private
14252     onResize : function(){
14253         this.refreshSize();
14254         this.syncBodyHeight();
14255         this.adjustAssets();
14256         this.focus();
14257         this.fireEvent("resize", this, this.size.width, this.size.height);
14258     },
14259
14260     // private
14261     onKeyDown : function(e){
14262         if(this.isVisible()){
14263             this.fireEvent("keydown", this, e);
14264         }
14265     },
14266
14267     /**
14268      * Resizes the dialog.
14269      * @param {Number} width
14270      * @param {Number} height
14271      * @return {Roo.BasicDialog} this
14272      */
14273     resizeTo : function(width, height){
14274         this.el.setSize(width, height);
14275         this.size = {width: width, height: height};
14276         this.syncBodyHeight();
14277         if(this.fixedcenter){
14278             this.center();
14279         }
14280         if(this.isVisible()){
14281             this.constrainXY();
14282             this.adjustAssets();
14283         }
14284         this.fireEvent("resize", this, width, height);
14285         return this;
14286     },
14287
14288
14289     /**
14290      * Resizes the dialog to fit the specified content size.
14291      * @param {Number} width
14292      * @param {Number} height
14293      * @return {Roo.BasicDialog} this
14294      */
14295     setContentSize : function(w, h){
14296         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14297         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14298         //if(!this.el.isBorderBox()){
14299             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14300             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14301         //}
14302         if(this.tabs){
14303             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14304             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14305         }
14306         this.resizeTo(w, h);
14307         return this;
14308     },
14309
14310     /**
14311      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14312      * executed in response to a particular key being pressed while the dialog is active.
14313      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14314      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14315      * @param {Function} fn The function to call
14316      * @param {Object} scope (optional) The scope of the function
14317      * @return {Roo.BasicDialog} this
14318      */
14319     addKeyListener : function(key, fn, scope){
14320         var keyCode, shift, ctrl, alt;
14321         if(typeof key == "object" && !(key instanceof Array)){
14322             keyCode = key["key"];
14323             shift = key["shift"];
14324             ctrl = key["ctrl"];
14325             alt = key["alt"];
14326         }else{
14327             keyCode = key;
14328         }
14329         var handler = function(dlg, e){
14330             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14331                 var k = e.getKey();
14332                 if(keyCode instanceof Array){
14333                     for(var i = 0, len = keyCode.length; i < len; i++){
14334                         if(keyCode[i] == k){
14335                           fn.call(scope || window, dlg, k, e);
14336                           return;
14337                         }
14338                     }
14339                 }else{
14340                     if(k == keyCode){
14341                         fn.call(scope || window, dlg, k, e);
14342                     }
14343                 }
14344             }
14345         };
14346         this.on("keydown", handler);
14347         return this;
14348     },
14349
14350     /**
14351      * Returns the TabPanel component (creates it if it doesn't exist).
14352      * Note: If you wish to simply check for the existence of tabs without creating them,
14353      * check for a null 'tabs' property.
14354      * @return {Roo.TabPanel} The tabs component
14355      */
14356     getTabs : function(){
14357         if(!this.tabs){
14358             this.el.addClass("x-dlg-auto-tabs");
14359             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14360             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14361         }
14362         return this.tabs;
14363     },
14364
14365     /**
14366      * Adds a button to the footer section of the dialog.
14367      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14368      * object or a valid Roo.DomHelper element config
14369      * @param {Function} handler The function called when the button is clicked
14370      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14371      * @return {Roo.Button} The new button
14372      */
14373     addButton : function(config, handler, scope){
14374         var dh = Roo.DomHelper;
14375         if(!this.footer){
14376             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14377         }
14378         if(!this.btnContainer){
14379             var tb = this.footer.createChild({
14380
14381                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14382                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14383             }, null, true);
14384             this.btnContainer = tb.firstChild.firstChild.firstChild;
14385         }
14386         var bconfig = {
14387             handler: handler,
14388             scope: scope,
14389             minWidth: this.minButtonWidth,
14390             hideParent:true
14391         };
14392         if(typeof config == "string"){
14393             bconfig.text = config;
14394         }else{
14395             if(config.tag){
14396                 bconfig.dhconfig = config;
14397             }else{
14398                 Roo.apply(bconfig, config);
14399             }
14400         }
14401         var fc = false;
14402         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14403             bconfig.position = Math.max(0, bconfig.position);
14404             fc = this.btnContainer.childNodes[bconfig.position];
14405         }
14406          
14407         var btn = new Roo.Button(
14408             fc ? 
14409                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14410                 : this.btnContainer.appendChild(document.createElement("td")),
14411             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14412             bconfig
14413         );
14414         this.syncBodyHeight();
14415         if(!this.buttons){
14416             /**
14417              * Array of all the buttons that have been added to this dialog via addButton
14418              * @type Array
14419              */
14420             this.buttons = [];
14421         }
14422         this.buttons.push(btn);
14423         return btn;
14424     },
14425
14426     /**
14427      * Sets the default button to be focused when the dialog is displayed.
14428      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14429      * @return {Roo.BasicDialog} this
14430      */
14431     setDefaultButton : function(btn){
14432         this.defaultButton = btn;
14433         return this;
14434     },
14435
14436     // private
14437     getHeaderFooterHeight : function(safe){
14438         var height = 0;
14439         if(this.header){
14440            height += this.header.getHeight();
14441         }
14442         if(this.footer){
14443            var fm = this.footer.getMargins();
14444             height += (this.footer.getHeight()+fm.top+fm.bottom);
14445         }
14446         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14447         height += this.centerBg.getPadding("tb");
14448         return height;
14449     },
14450
14451     // private
14452     syncBodyHeight : function(){
14453         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14454         var height = this.size.height - this.getHeaderFooterHeight(false);
14455         bd.setHeight(height-bd.getMargins("tb"));
14456         var hh = this.header.getHeight();
14457         var h = this.size.height-hh;
14458         cb.setHeight(h);
14459         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14460         bw.setHeight(h-cb.getPadding("tb"));
14461         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14462         bd.setWidth(bw.getWidth(true));
14463         if(this.tabs){
14464             this.tabs.syncHeight();
14465             if(Roo.isIE){
14466                 this.tabs.el.repaint();
14467             }
14468         }
14469     },
14470
14471     /**
14472      * Restores the previous state of the dialog if Roo.state is configured.
14473      * @return {Roo.BasicDialog} this
14474      */
14475     restoreState : function(){
14476         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14477         if(box && box.width){
14478             this.xy = [box.x, box.y];
14479             this.resizeTo(box.width, box.height);
14480         }
14481         return this;
14482     },
14483
14484     // private
14485     beforeShow : function(){
14486         this.expand();
14487         if(this.fixedcenter){
14488             this.xy = this.el.getCenterXY(true);
14489         }
14490         if(this.modal){
14491             Roo.get(document.body).addClass("x-body-masked");
14492             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14493             this.mask.show();
14494         }
14495         this.constrainXY();
14496     },
14497
14498     // private
14499     animShow : function(){
14500         var b = Roo.get(this.animateTarget, true).getBox();
14501         this.proxy.setSize(b.width, b.height);
14502         this.proxy.setLocation(b.x, b.y);
14503         this.proxy.show();
14504         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14505                     true, .35, this.showEl.createDelegate(this));
14506     },
14507
14508     /**
14509      * Shows the dialog.
14510      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14511      * @return {Roo.BasicDialog} this
14512      */
14513     show : function(animateTarget){
14514         if (this.fireEvent("beforeshow", this) === false){
14515             return;
14516         }
14517         if(this.syncHeightBeforeShow){
14518             this.syncBodyHeight();
14519         }else if(this.firstShow){
14520             this.firstShow = false;
14521             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14522         }
14523         this.animateTarget = animateTarget || this.animateTarget;
14524         if(!this.el.isVisible()){
14525             this.beforeShow();
14526             if(this.animateTarget){
14527                 this.animShow();
14528             }else{
14529                 this.showEl();
14530             }
14531         }
14532         return this;
14533     },
14534
14535     // private
14536     showEl : function(){
14537         this.proxy.hide();
14538         this.el.setXY(this.xy);
14539         this.el.show();
14540         this.adjustAssets(true);
14541         this.toFront();
14542         this.focus();
14543         // IE peekaboo bug - fix found by Dave Fenwick
14544         if(Roo.isIE){
14545             this.el.repaint();
14546         }
14547         this.fireEvent("show", this);
14548     },
14549
14550     /**
14551      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14552      * dialog itself will receive focus.
14553      */
14554     focus : function(){
14555         if(this.defaultButton){
14556             this.defaultButton.focus();
14557         }else{
14558             this.focusEl.focus();
14559         }
14560     },
14561
14562     // private
14563     constrainXY : function(){
14564         if(this.constraintoviewport !== false){
14565             if(!this.viewSize){
14566                 if(this.container){
14567                     var s = this.container.getSize();
14568                     this.viewSize = [s.width, s.height];
14569                 }else{
14570                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14571                 }
14572             }
14573             var s = Roo.get(this.container||document).getScroll();
14574
14575             var x = this.xy[0], y = this.xy[1];
14576             var w = this.size.width, h = this.size.height;
14577             var vw = this.viewSize[0], vh = this.viewSize[1];
14578             // only move it if it needs it
14579             var moved = false;
14580             // first validate right/bottom
14581             if(x + w > vw+s.left){
14582                 x = vw - w;
14583                 moved = true;
14584             }
14585             if(y + h > vh+s.top){
14586                 y = vh - h;
14587                 moved = true;
14588             }
14589             // then make sure top/left isn't negative
14590             if(x < s.left){
14591                 x = s.left;
14592                 moved = true;
14593             }
14594             if(y < s.top){
14595                 y = s.top;
14596                 moved = true;
14597             }
14598             if(moved){
14599                 // cache xy
14600                 this.xy = [x, y];
14601                 if(this.isVisible()){
14602                     this.el.setLocation(x, y);
14603                     this.adjustAssets();
14604                 }
14605             }
14606         }
14607     },
14608
14609     // private
14610     onDrag : function(){
14611         if(!this.proxyDrag){
14612             this.xy = this.el.getXY();
14613             this.adjustAssets();
14614         }
14615     },
14616
14617     // private
14618     adjustAssets : function(doShow){
14619         var x = this.xy[0], y = this.xy[1];
14620         var w = this.size.width, h = this.size.height;
14621         if(doShow === true){
14622             if(this.shadow){
14623                 this.shadow.show(this.el);
14624             }
14625             if(this.shim){
14626                 this.shim.show();
14627             }
14628         }
14629         if(this.shadow && this.shadow.isVisible()){
14630             this.shadow.show(this.el);
14631         }
14632         if(this.shim && this.shim.isVisible()){
14633             this.shim.setBounds(x, y, w, h);
14634         }
14635     },
14636
14637     // private
14638     adjustViewport : function(w, h){
14639         if(!w || !h){
14640             w = Roo.lib.Dom.getViewWidth();
14641             h = Roo.lib.Dom.getViewHeight();
14642         }
14643         // cache the size
14644         this.viewSize = [w, h];
14645         if(this.modal && this.mask.isVisible()){
14646             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14647             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14648         }
14649         if(this.isVisible()){
14650             this.constrainXY();
14651         }
14652     },
14653
14654     /**
14655      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14656      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14657      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14658      */
14659     destroy : function(removeEl){
14660         if(this.isVisible()){
14661             this.animateTarget = null;
14662             this.hide();
14663         }
14664         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14665         if(this.tabs){
14666             this.tabs.destroy(removeEl);
14667         }
14668         Roo.destroy(
14669              this.shim,
14670              this.proxy,
14671              this.resizer,
14672              this.close,
14673              this.mask
14674         );
14675         if(this.dd){
14676             this.dd.unreg();
14677         }
14678         if(this.buttons){
14679            for(var i = 0, len = this.buttons.length; i < len; i++){
14680                this.buttons[i].destroy();
14681            }
14682         }
14683         this.el.removeAllListeners();
14684         if(removeEl === true){
14685             this.el.update("");
14686             this.el.remove();
14687         }
14688         Roo.DialogManager.unregister(this);
14689     },
14690
14691     // private
14692     startMove : function(){
14693         if(this.proxyDrag){
14694             this.proxy.show();
14695         }
14696         if(this.constraintoviewport !== false){
14697             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14698         }
14699     },
14700
14701     // private
14702     endMove : function(){
14703         if(!this.proxyDrag){
14704             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14705         }else{
14706             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14707             this.proxy.hide();
14708         }
14709         this.refreshSize();
14710         this.adjustAssets();
14711         this.focus();
14712         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14713     },
14714
14715     /**
14716      * Brings this dialog to the front of any other visible dialogs
14717      * @return {Roo.BasicDialog} this
14718      */
14719     toFront : function(){
14720         Roo.DialogManager.bringToFront(this);
14721         return this;
14722     },
14723
14724     /**
14725      * Sends this dialog to the back (under) of any other visible dialogs
14726      * @return {Roo.BasicDialog} this
14727      */
14728     toBack : function(){
14729         Roo.DialogManager.sendToBack(this);
14730         return this;
14731     },
14732
14733     /**
14734      * Centers this dialog in the viewport
14735      * @return {Roo.BasicDialog} this
14736      */
14737     center : function(){
14738         var xy = this.el.getCenterXY(true);
14739         this.moveTo(xy[0], xy[1]);
14740         return this;
14741     },
14742
14743     /**
14744      * Moves the dialog's top-left corner to the specified point
14745      * @param {Number} x
14746      * @param {Number} y
14747      * @return {Roo.BasicDialog} this
14748      */
14749     moveTo : function(x, y){
14750         this.xy = [x,y];
14751         if(this.isVisible()){
14752             this.el.setXY(this.xy);
14753             this.adjustAssets();
14754         }
14755         return this;
14756     },
14757
14758     /**
14759      * Aligns the dialog to the specified element
14760      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14761      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14762      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14763      * @return {Roo.BasicDialog} this
14764      */
14765     alignTo : function(element, position, offsets){
14766         this.xy = this.el.getAlignToXY(element, position, offsets);
14767         if(this.isVisible()){
14768             this.el.setXY(this.xy);
14769             this.adjustAssets();
14770         }
14771         return this;
14772     },
14773
14774     /**
14775      * Anchors an element to another element and realigns it when the window is resized.
14776      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14777      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14778      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14779      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14780      * is a number, it is used as the buffer delay (defaults to 50ms).
14781      * @return {Roo.BasicDialog} this
14782      */
14783     anchorTo : function(el, alignment, offsets, monitorScroll){
14784         var action = function(){
14785             this.alignTo(el, alignment, offsets);
14786         };
14787         Roo.EventManager.onWindowResize(action, this);
14788         var tm = typeof monitorScroll;
14789         if(tm != 'undefined'){
14790             Roo.EventManager.on(window, 'scroll', action, this,
14791                 {buffer: tm == 'number' ? monitorScroll : 50});
14792         }
14793         action.call(this);
14794         return this;
14795     },
14796
14797     /**
14798      * Returns true if the dialog is visible
14799      * @return {Boolean}
14800      */
14801     isVisible : function(){
14802         return this.el.isVisible();
14803     },
14804
14805     // private
14806     animHide : function(callback){
14807         var b = Roo.get(this.animateTarget).getBox();
14808         this.proxy.show();
14809         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14810         this.el.hide();
14811         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14812                     this.hideEl.createDelegate(this, [callback]));
14813     },
14814
14815     /**
14816      * Hides the dialog.
14817      * @param {Function} callback (optional) Function to call when the dialog is hidden
14818      * @return {Roo.BasicDialog} this
14819      */
14820     hide : function(callback){
14821         if (this.fireEvent("beforehide", this) === false){
14822             return;
14823         }
14824         if(this.shadow){
14825             this.shadow.hide();
14826         }
14827         if(this.shim) {
14828           this.shim.hide();
14829         }
14830         if(this.animateTarget){
14831            this.animHide(callback);
14832         }else{
14833             this.el.hide();
14834             this.hideEl(callback);
14835         }
14836         return this;
14837     },
14838
14839     // private
14840     hideEl : function(callback){
14841         this.proxy.hide();
14842         if(this.modal){
14843             this.mask.hide();
14844             Roo.get(document.body).removeClass("x-body-masked");
14845         }
14846         this.fireEvent("hide", this);
14847         if(typeof callback == "function"){
14848             callback();
14849         }
14850     },
14851
14852     // private
14853     hideAction : function(){
14854         this.setLeft("-10000px");
14855         this.setTop("-10000px");
14856         this.setStyle("visibility", "hidden");
14857     },
14858
14859     // private
14860     refreshSize : function(){
14861         this.size = this.el.getSize();
14862         this.xy = this.el.getXY();
14863         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14864     },
14865
14866     // private
14867     // z-index is managed by the DialogManager and may be overwritten at any time
14868     setZIndex : function(index){
14869         if(this.modal){
14870             this.mask.setStyle("z-index", index);
14871         }
14872         if(this.shim){
14873             this.shim.setStyle("z-index", ++index);
14874         }
14875         if(this.shadow){
14876             this.shadow.setZIndex(++index);
14877         }
14878         this.el.setStyle("z-index", ++index);
14879         if(this.proxy){
14880             this.proxy.setStyle("z-index", ++index);
14881         }
14882         if(this.resizer){
14883             this.resizer.proxy.setStyle("z-index", ++index);
14884         }
14885
14886         this.lastZIndex = index;
14887     },
14888
14889     /**
14890      * Returns the element for this dialog
14891      * @return {Roo.Element} The underlying dialog Element
14892      */
14893     getEl : function(){
14894         return this.el;
14895     }
14896 });
14897
14898 /**
14899  * @class Roo.DialogManager
14900  * Provides global access to BasicDialogs that have been created and
14901  * support for z-indexing (layering) multiple open dialogs.
14902  */
14903 Roo.DialogManager = function(){
14904     var list = {};
14905     var accessList = [];
14906     var front = null;
14907
14908     // private
14909     var sortDialogs = function(d1, d2){
14910         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14911     };
14912
14913     // private
14914     var orderDialogs = function(){
14915         accessList.sort(sortDialogs);
14916         var seed = Roo.DialogManager.zseed;
14917         for(var i = 0, len = accessList.length; i < len; i++){
14918             var dlg = accessList[i];
14919             if(dlg){
14920                 dlg.setZIndex(seed + (i*10));
14921             }
14922         }
14923     };
14924
14925     return {
14926         /**
14927          * The starting z-index for BasicDialogs (defaults to 9000)
14928          * @type Number The z-index value
14929          */
14930         zseed : 9000,
14931
14932         // private
14933         register : function(dlg){
14934             list[dlg.id] = dlg;
14935             accessList.push(dlg);
14936         },
14937
14938         // private
14939         unregister : function(dlg){
14940             delete list[dlg.id];
14941             var i=0;
14942             var len=0;
14943             if(!accessList.indexOf){
14944                 for(  i = 0, len = accessList.length; i < len; i++){
14945                     if(accessList[i] == dlg){
14946                         accessList.splice(i, 1);
14947                         return;
14948                     }
14949                 }
14950             }else{
14951                  i = accessList.indexOf(dlg);
14952                 if(i != -1){
14953                     accessList.splice(i, 1);
14954                 }
14955             }
14956         },
14957
14958         /**
14959          * Gets a registered dialog by id
14960          * @param {String/Object} id The id of the dialog or a dialog
14961          * @return {Roo.BasicDialog} this
14962          */
14963         get : function(id){
14964             return typeof id == "object" ? id : list[id];
14965         },
14966
14967         /**
14968          * Brings the specified dialog to the front
14969          * @param {String/Object} dlg The id of the dialog or a dialog
14970          * @return {Roo.BasicDialog} this
14971          */
14972         bringToFront : function(dlg){
14973             dlg = this.get(dlg);
14974             if(dlg != front){
14975                 front = dlg;
14976                 dlg._lastAccess = new Date().getTime();
14977                 orderDialogs();
14978             }
14979             return dlg;
14980         },
14981
14982         /**
14983          * Sends the specified dialog to the back
14984          * @param {String/Object} dlg The id of the dialog or a dialog
14985          * @return {Roo.BasicDialog} this
14986          */
14987         sendToBack : function(dlg){
14988             dlg = this.get(dlg);
14989             dlg._lastAccess = -(new Date().getTime());
14990             orderDialogs();
14991             return dlg;
14992         },
14993
14994         /**
14995          * Hides all dialogs
14996          */
14997         hideAll : function(){
14998             for(var id in list){
14999                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15000                     list[id].hide();
15001                 }
15002             }
15003         }
15004     };
15005 }();
15006
15007 /**
15008  * @class Roo.LayoutDialog
15009  * @extends Roo.BasicDialog
15010  * Dialog which provides adjustments for working with a layout in a Dialog.
15011  * Add your necessary layout config options to the dialog's config.<br>
15012  * Example usage (including a nested layout):
15013  * <pre><code>
15014 if(!dialog){
15015     dialog = new Roo.LayoutDialog("download-dlg", {
15016         modal: true,
15017         width:600,
15018         height:450,
15019         shadow:true,
15020         minWidth:500,
15021         minHeight:350,
15022         autoTabs:true,
15023         proxyDrag:true,
15024         // layout config merges with the dialog config
15025         center:{
15026             tabPosition: "top",
15027             alwaysShowTabs: true
15028         }
15029     });
15030     dialog.addKeyListener(27, dialog.hide, dialog);
15031     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15032     dialog.addButton("Build It!", this.getDownload, this);
15033
15034     // we can even add nested layouts
15035     var innerLayout = new Roo.BorderLayout("dl-inner", {
15036         east: {
15037             initialSize: 200,
15038             autoScroll:true,
15039             split:true
15040         },
15041         center: {
15042             autoScroll:true
15043         }
15044     });
15045     innerLayout.beginUpdate();
15046     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15047     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15048     innerLayout.endUpdate(true);
15049
15050     var layout = dialog.getLayout();
15051     layout.beginUpdate();
15052     layout.add("center", new Roo.ContentPanel("standard-panel",
15053                         {title: "Download the Source", fitToFrame:true}));
15054     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15055                {title: "Build your own roo.js"}));
15056     layout.getRegion("center").showPanel(sp);
15057     layout.endUpdate();
15058 }
15059 </code></pre>
15060     * @constructor
15061     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15062     * @param {Object} config configuration options
15063   */
15064 Roo.LayoutDialog = function(el, cfg){
15065     
15066     var config=  cfg;
15067     if (typeof(cfg) == 'undefined') {
15068         config = Roo.apply({}, el);
15069         el = Roo.get( document.documentElement || document.body).createChild();
15070         //config.autoCreate = true;
15071     }
15072     
15073     
15074     config.autoTabs = false;
15075     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15076     this.body.setStyle({overflow:"hidden", position:"relative"});
15077     this.layout = new Roo.BorderLayout(this.body.dom, config);
15078     this.layout.monitorWindowResize = false;
15079     this.el.addClass("x-dlg-auto-layout");
15080     // fix case when center region overwrites center function
15081     this.center = Roo.BasicDialog.prototype.center;
15082     this.on("show", this.layout.layout, this.layout, true);
15083     if (config.items) {
15084         var xitems = config.items;
15085         delete config.items;
15086         Roo.each(xitems, this.addxtype, this);
15087     }
15088     
15089     
15090 };
15091 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15092     /**
15093      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15094      * @deprecated
15095      */
15096     endUpdate : function(){
15097         this.layout.endUpdate();
15098     },
15099
15100     /**
15101      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15102      *  @deprecated
15103      */
15104     beginUpdate : function(){
15105         this.layout.beginUpdate();
15106     },
15107
15108     /**
15109      * Get the BorderLayout for this dialog
15110      * @return {Roo.BorderLayout}
15111      */
15112     getLayout : function(){
15113         return this.layout;
15114     },
15115
15116     showEl : function(){
15117         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15118         if(Roo.isIE7){
15119             this.layout.layout();
15120         }
15121     },
15122
15123     // private
15124     // Use the syncHeightBeforeShow config option to control this automatically
15125     syncBodyHeight : function(){
15126         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15127         if(this.layout){this.layout.layout();}
15128     },
15129     
15130       /**
15131      * Add an xtype element (actually adds to the layout.)
15132      * @return {Object} xdata xtype object data.
15133      */
15134     
15135     addxtype : function(c) {
15136         return this.layout.addxtype(c);
15137     }
15138 });/*
15139  * Based on:
15140  * Ext JS Library 1.1.1
15141  * Copyright(c) 2006-2007, Ext JS, LLC.
15142  *
15143  * Originally Released Under LGPL - original licence link has changed is not relivant.
15144  *
15145  * Fork - LGPL
15146  * <script type="text/javascript">
15147  */
15148  
15149 /**
15150  * @class Roo.MessageBox
15151  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15152  * Example usage:
15153  *<pre><code>
15154 // Basic alert:
15155 Roo.Msg.alert('Status', 'Changes saved successfully.');
15156
15157 // Prompt for user data:
15158 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15159     if (btn == 'ok'){
15160         // process text value...
15161     }
15162 });
15163
15164 // Show a dialog using config options:
15165 Roo.Msg.show({
15166    title:'Save Changes?',
15167    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15168    buttons: Roo.Msg.YESNOCANCEL,
15169    fn: processResult,
15170    animEl: 'elId'
15171 });
15172 </code></pre>
15173  * @singleton
15174  */
15175 Roo.MessageBox = function(){
15176     var dlg, opt, mask, waitTimer;
15177     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15178     var buttons, activeTextEl, bwidth;
15179
15180     // private
15181     var handleButton = function(button){
15182         dlg.hide();
15183         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15184     };
15185
15186     // private
15187     var handleHide = function(){
15188         if(opt && opt.cls){
15189             dlg.el.removeClass(opt.cls);
15190         }
15191         if(waitTimer){
15192             Roo.TaskMgr.stop(waitTimer);
15193             waitTimer = null;
15194         }
15195     };
15196
15197     // private
15198     var updateButtons = function(b){
15199         var width = 0;
15200         if(!b){
15201             buttons["ok"].hide();
15202             buttons["cancel"].hide();
15203             buttons["yes"].hide();
15204             buttons["no"].hide();
15205             dlg.footer.dom.style.display = 'none';
15206             return width;
15207         }
15208         dlg.footer.dom.style.display = '';
15209         for(var k in buttons){
15210             if(typeof buttons[k] != "function"){
15211                 if(b[k]){
15212                     buttons[k].show();
15213                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15214                     width += buttons[k].el.getWidth()+15;
15215                 }else{
15216                     buttons[k].hide();
15217                 }
15218             }
15219         }
15220         return width;
15221     };
15222
15223     // private
15224     var handleEsc = function(d, k, e){
15225         if(opt && opt.closable !== false){
15226             dlg.hide();
15227         }
15228         if(e){
15229             e.stopEvent();
15230         }
15231     };
15232
15233     return {
15234         /**
15235          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15236          * @return {Roo.BasicDialog} The BasicDialog element
15237          */
15238         getDialog : function(){
15239            if(!dlg){
15240                 dlg = new Roo.BasicDialog("x-msg-box", {
15241                     autoCreate : true,
15242                     shadow: true,
15243                     draggable: true,
15244                     resizable:false,
15245                     constraintoviewport:false,
15246                     fixedcenter:true,
15247                     collapsible : false,
15248                     shim:true,
15249                     modal: true,
15250                     width:400, height:100,
15251                     buttonAlign:"center",
15252                     closeClick : function(){
15253                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15254                             handleButton("no");
15255                         }else{
15256                             handleButton("cancel");
15257                         }
15258                     }
15259                 });
15260                 dlg.on("hide", handleHide);
15261                 mask = dlg.mask;
15262                 dlg.addKeyListener(27, handleEsc);
15263                 buttons = {};
15264                 var bt = this.buttonText;
15265                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15266                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15267                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15268                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15269                 bodyEl = dlg.body.createChild({
15270
15271                     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>'
15272                 });
15273                 msgEl = bodyEl.dom.firstChild;
15274                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15275                 textboxEl.enableDisplayMode();
15276                 textboxEl.addKeyListener([10,13], function(){
15277                     if(dlg.isVisible() && opt && opt.buttons){
15278                         if(opt.buttons.ok){
15279                             handleButton("ok");
15280                         }else if(opt.buttons.yes){
15281                             handleButton("yes");
15282                         }
15283                     }
15284                 });
15285                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15286                 textareaEl.enableDisplayMode();
15287                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15288                 progressEl.enableDisplayMode();
15289                 var pf = progressEl.dom.firstChild;
15290                 if (pf) {
15291                     pp = Roo.get(pf.firstChild);
15292                     pp.setHeight(pf.offsetHeight);
15293                 }
15294                 
15295             }
15296             return dlg;
15297         },
15298
15299         /**
15300          * Updates the message box body text
15301          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15302          * the XHTML-compliant non-breaking space character '&amp;#160;')
15303          * @return {Roo.MessageBox} This message box
15304          */
15305         updateText : function(text){
15306             if(!dlg.isVisible() && !opt.width){
15307                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15308             }
15309             msgEl.innerHTML = text || '&#160;';
15310             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15311                         Math.max(opt.minWidth || this.minWidth, bwidth));
15312             if(opt.prompt){
15313                 activeTextEl.setWidth(w);
15314             }
15315             if(dlg.isVisible()){
15316                 dlg.fixedcenter = false;
15317             }
15318             dlg.setContentSize(w, bodyEl.getHeight());
15319             if(dlg.isVisible()){
15320                 dlg.fixedcenter = true;
15321             }
15322             return this;
15323         },
15324
15325         /**
15326          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15327          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15328          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15329          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15330          * @return {Roo.MessageBox} This message box
15331          */
15332         updateProgress : function(value, text){
15333             if(text){
15334                 this.updateText(text);
15335             }
15336             if (pp) { // weird bug on my firefox - for some reason this is not defined
15337                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15338             }
15339             return this;
15340         },        
15341
15342         /**
15343          * Returns true if the message box is currently displayed
15344          * @return {Boolean} True if the message box is visible, else false
15345          */
15346         isVisible : function(){
15347             return dlg && dlg.isVisible();  
15348         },
15349
15350         /**
15351          * Hides the message box if it is displayed
15352          */
15353         hide : function(){
15354             if(this.isVisible()){
15355                 dlg.hide();
15356             }  
15357         },
15358
15359         /**
15360          * Displays a new message box, or reinitializes an existing message box, based on the config options
15361          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15362          * The following config object properties are supported:
15363          * <pre>
15364 Property    Type             Description
15365 ----------  ---------------  ------------------------------------------------------------------------------------
15366 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15367                                    closes (defaults to undefined)
15368 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15369                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15370 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15371                                    progress and wait dialogs will ignore this property and always hide the
15372                                    close button as they can only be closed programmatically.
15373 cls               String           A custom CSS class to apply to the message box element
15374 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15375                                    displayed (defaults to 75)
15376 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15377                                    function will be btn (the name of the button that was clicked, if applicable,
15378                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15379                                    Progress and wait dialogs will ignore this option since they do not respond to
15380                                    user actions and can only be closed programmatically, so any required function
15381                                    should be called by the same code after it closes the dialog.
15382 icon              String           A CSS class that provides a background image to be used as an icon for
15383                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15384 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15385 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15386 modal             Boolean          False to allow user interaction with the page while the message box is
15387                                    displayed (defaults to true)
15388 msg               String           A string that will replace the existing message box body text (defaults
15389                                    to the XHTML-compliant non-breaking space character '&#160;')
15390 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15391 progress          Boolean          True to display a progress bar (defaults to false)
15392 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15393 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15394 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15395 title             String           The title text
15396 value             String           The string value to set into the active textbox element if displayed
15397 wait              Boolean          True to display a progress bar (defaults to false)
15398 width             Number           The width of the dialog in pixels
15399 </pre>
15400          *
15401          * Example usage:
15402          * <pre><code>
15403 Roo.Msg.show({
15404    title: 'Address',
15405    msg: 'Please enter your address:',
15406    width: 300,
15407    buttons: Roo.MessageBox.OKCANCEL,
15408    multiline: true,
15409    fn: saveAddress,
15410    animEl: 'addAddressBtn'
15411 });
15412 </code></pre>
15413          * @param {Object} config Configuration options
15414          * @return {Roo.MessageBox} This message box
15415          */
15416         show : function(options){
15417             if(this.isVisible()){
15418                 this.hide();
15419             }
15420             var d = this.getDialog();
15421             opt = options;
15422             d.setTitle(opt.title || "&#160;");
15423             d.close.setDisplayed(opt.closable !== false);
15424             activeTextEl = textboxEl;
15425             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15426             if(opt.prompt){
15427                 if(opt.multiline){
15428                     textboxEl.hide();
15429                     textareaEl.show();
15430                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15431                         opt.multiline : this.defaultTextHeight);
15432                     activeTextEl = textareaEl;
15433                 }else{
15434                     textboxEl.show();
15435                     textareaEl.hide();
15436                 }
15437             }else{
15438                 textboxEl.hide();
15439                 textareaEl.hide();
15440             }
15441             progressEl.setDisplayed(opt.progress === true);
15442             this.updateProgress(0);
15443             activeTextEl.dom.value = opt.value || "";
15444             if(opt.prompt){
15445                 dlg.setDefaultButton(activeTextEl);
15446             }else{
15447                 var bs = opt.buttons;
15448                 var db = null;
15449                 if(bs && bs.ok){
15450                     db = buttons["ok"];
15451                 }else if(bs && bs.yes){
15452                     db = buttons["yes"];
15453                 }
15454                 dlg.setDefaultButton(db);
15455             }
15456             bwidth = updateButtons(opt.buttons);
15457             this.updateText(opt.msg);
15458             if(opt.cls){
15459                 d.el.addClass(opt.cls);
15460             }
15461             d.proxyDrag = opt.proxyDrag === true;
15462             d.modal = opt.modal !== false;
15463             d.mask = opt.modal !== false ? mask : false;
15464             if(!d.isVisible()){
15465                 // force it to the end of the z-index stack so it gets a cursor in FF
15466                 document.body.appendChild(dlg.el.dom);
15467                 d.animateTarget = null;
15468                 d.show(options.animEl);
15469             }
15470             return this;
15471         },
15472
15473         /**
15474          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15475          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15476          * and closing the message box when the process is complete.
15477          * @param {String} title The title bar text
15478          * @param {String} msg The message box body text
15479          * @return {Roo.MessageBox} This message box
15480          */
15481         progress : function(title, msg){
15482             this.show({
15483                 title : title,
15484                 msg : msg,
15485                 buttons: false,
15486                 progress:true,
15487                 closable:false,
15488                 minWidth: this.minProgressWidth,
15489                 modal : true
15490             });
15491             return this;
15492         },
15493
15494         /**
15495          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15496          * If a callback function is passed it will be called after the user clicks the button, and the
15497          * id of the button that was clicked will be passed as the only parameter to the callback
15498          * (could also be the top-right close button).
15499          * @param {String} title The title bar text
15500          * @param {String} msg The message box body text
15501          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15502          * @param {Object} scope (optional) The scope of the callback function
15503          * @return {Roo.MessageBox} This message box
15504          */
15505         alert : function(title, msg, fn, scope){
15506             this.show({
15507                 title : title,
15508                 msg : msg,
15509                 buttons: this.OK,
15510                 fn: fn,
15511                 scope : scope,
15512                 modal : true
15513             });
15514             return this;
15515         },
15516
15517         /**
15518          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15519          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15520          * You are responsible for closing the message box when the process is complete.
15521          * @param {String} msg The message box body text
15522          * @param {String} title (optional) The title bar text
15523          * @return {Roo.MessageBox} This message box
15524          */
15525         wait : function(msg, title){
15526             this.show({
15527                 title : title,
15528                 msg : msg,
15529                 buttons: false,
15530                 closable:false,
15531                 progress:true,
15532                 modal:true,
15533                 width:300,
15534                 wait:true
15535             });
15536             waitTimer = Roo.TaskMgr.start({
15537                 run: function(i){
15538                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15539                 },
15540                 interval: 1000
15541             });
15542             return this;
15543         },
15544
15545         /**
15546          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15547          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15548          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15549          * @param {String} title The title bar text
15550          * @param {String} msg The message box body text
15551          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15552          * @param {Object} scope (optional) The scope of the callback function
15553          * @return {Roo.MessageBox} This message box
15554          */
15555         confirm : function(title, msg, fn, scope){
15556             this.show({
15557                 title : title,
15558                 msg : msg,
15559                 buttons: this.YESNO,
15560                 fn: fn,
15561                 scope : scope,
15562                 modal : true
15563             });
15564             return this;
15565         },
15566
15567         /**
15568          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15569          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15570          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15571          * (could also be the top-right close button) and the text that was entered will be passed as the two
15572          * parameters to the callback.
15573          * @param {String} title The title bar text
15574          * @param {String} msg The message box body text
15575          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15576          * @param {Object} scope (optional) The scope of the callback function
15577          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15578          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15579          * @return {Roo.MessageBox} This message box
15580          */
15581         prompt : function(title, msg, fn, scope, multiline){
15582             this.show({
15583                 title : title,
15584                 msg : msg,
15585                 buttons: this.OKCANCEL,
15586                 fn: fn,
15587                 minWidth:250,
15588                 scope : scope,
15589                 prompt:true,
15590                 multiline: multiline,
15591                 modal : true
15592             });
15593             return this;
15594         },
15595
15596         /**
15597          * Button config that displays a single OK button
15598          * @type Object
15599          */
15600         OK : {ok:true},
15601         /**
15602          * Button config that displays Yes and No buttons
15603          * @type Object
15604          */
15605         YESNO : {yes:true, no:true},
15606         /**
15607          * Button config that displays OK and Cancel buttons
15608          * @type Object
15609          */
15610         OKCANCEL : {ok:true, cancel:true},
15611         /**
15612          * Button config that displays Yes, No and Cancel buttons
15613          * @type Object
15614          */
15615         YESNOCANCEL : {yes:true, no:true, cancel:true},
15616
15617         /**
15618          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15619          * @type Number
15620          */
15621         defaultTextHeight : 75,
15622         /**
15623          * The maximum width in pixels of the message box (defaults to 600)
15624          * @type Number
15625          */
15626         maxWidth : 600,
15627         /**
15628          * The minimum width in pixels of the message box (defaults to 100)
15629          * @type Number
15630          */
15631         minWidth : 100,
15632         /**
15633          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15634          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15635          * @type Number
15636          */
15637         minProgressWidth : 250,
15638         /**
15639          * An object containing the default button text strings that can be overriden for localized language support.
15640          * Supported properties are: ok, cancel, yes and no.
15641          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15642          * @type Object
15643          */
15644         buttonText : {
15645             ok : "OK",
15646             cancel : "Cancel",
15647             yes : "Yes",
15648             no : "No"
15649         }
15650     };
15651 }();
15652
15653 /**
15654  * Shorthand for {@link Roo.MessageBox}
15655  */
15656 Roo.Msg = Roo.MessageBox;/*
15657  * Based on:
15658  * Ext JS Library 1.1.1
15659  * Copyright(c) 2006-2007, Ext JS, LLC.
15660  *
15661  * Originally Released Under LGPL - original licence link has changed is not relivant.
15662  *
15663  * Fork - LGPL
15664  * <script type="text/javascript">
15665  */
15666 /**
15667  * @class Roo.QuickTips
15668  * Provides attractive and customizable tooltips for any element.
15669  * @singleton
15670  */
15671 Roo.QuickTips = function(){
15672     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15673     var ce, bd, xy, dd;
15674     var visible = false, disabled = true, inited = false;
15675     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15676     
15677     var onOver = function(e){
15678         if(disabled){
15679             return;
15680         }
15681         var t = e.getTarget();
15682         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15683             return;
15684         }
15685         if(ce && t == ce.el){
15686             clearTimeout(hideProc);
15687             return;
15688         }
15689         if(t && tagEls[t.id]){
15690             tagEls[t.id].el = t;
15691             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15692             return;
15693         }
15694         var ttp, et = Roo.fly(t);
15695         var ns = cfg.namespace;
15696         if(tm.interceptTitles && t.title){
15697             ttp = t.title;
15698             t.qtip = ttp;
15699             t.removeAttribute("title");
15700             e.preventDefault();
15701         }else{
15702             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15703         }
15704         if(ttp){
15705             showProc = show.defer(tm.showDelay, tm, [{
15706                 el: t, 
15707                 text: ttp, 
15708                 width: et.getAttributeNS(ns, cfg.width),
15709                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15710                 title: et.getAttributeNS(ns, cfg.title),
15711                     cls: et.getAttributeNS(ns, cfg.cls)
15712             }]);
15713         }
15714     };
15715     
15716     var onOut = function(e){
15717         clearTimeout(showProc);
15718         var t = e.getTarget();
15719         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15720             hideProc = setTimeout(hide, tm.hideDelay);
15721         }
15722     };
15723     
15724     var onMove = function(e){
15725         if(disabled){
15726             return;
15727         }
15728         xy = e.getXY();
15729         xy[1] += 18;
15730         if(tm.trackMouse && ce){
15731             el.setXY(xy);
15732         }
15733     };
15734     
15735     var onDown = function(e){
15736         clearTimeout(showProc);
15737         clearTimeout(hideProc);
15738         if(!e.within(el)){
15739             if(tm.hideOnClick){
15740                 hide();
15741                 tm.disable();
15742                 tm.enable.defer(100, tm);
15743             }
15744         }
15745     };
15746     
15747     var getPad = function(){
15748         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15749     };
15750
15751     var show = function(o){
15752         if(disabled){
15753             return;
15754         }
15755         clearTimeout(dismissProc);
15756         ce = o;
15757         if(removeCls){ // in case manually hidden
15758             el.removeClass(removeCls);
15759             removeCls = null;
15760         }
15761         if(ce.cls){
15762             el.addClass(ce.cls);
15763             removeCls = ce.cls;
15764         }
15765         if(ce.title){
15766             tipTitle.update(ce.title);
15767             tipTitle.show();
15768         }else{
15769             tipTitle.update('');
15770             tipTitle.hide();
15771         }
15772         el.dom.style.width  = tm.maxWidth+'px';
15773         //tipBody.dom.style.width = '';
15774         tipBodyText.update(o.text);
15775         var p = getPad(), w = ce.width;
15776         if(!w){
15777             var td = tipBodyText.dom;
15778             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15779             if(aw > tm.maxWidth){
15780                 w = tm.maxWidth;
15781             }else if(aw < tm.minWidth){
15782                 w = tm.minWidth;
15783             }else{
15784                 w = aw;
15785             }
15786         }
15787         //tipBody.setWidth(w);
15788         el.setWidth(parseInt(w, 10) + p);
15789         if(ce.autoHide === false){
15790             close.setDisplayed(true);
15791             if(dd){
15792                 dd.unlock();
15793             }
15794         }else{
15795             close.setDisplayed(false);
15796             if(dd){
15797                 dd.lock();
15798             }
15799         }
15800         if(xy){
15801             el.avoidY = xy[1]-18;
15802             el.setXY(xy);
15803         }
15804         if(tm.animate){
15805             el.setOpacity(.1);
15806             el.setStyle("visibility", "visible");
15807             el.fadeIn({callback: afterShow});
15808         }else{
15809             afterShow();
15810         }
15811     };
15812     
15813     var afterShow = function(){
15814         if(ce){
15815             el.show();
15816             esc.enable();
15817             if(tm.autoDismiss && ce.autoHide !== false){
15818                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15819             }
15820         }
15821     };
15822     
15823     var hide = function(noanim){
15824         clearTimeout(dismissProc);
15825         clearTimeout(hideProc);
15826         ce = null;
15827         if(el.isVisible()){
15828             esc.disable();
15829             if(noanim !== true && tm.animate){
15830                 el.fadeOut({callback: afterHide});
15831             }else{
15832                 afterHide();
15833             } 
15834         }
15835     };
15836     
15837     var afterHide = function(){
15838         el.hide();
15839         if(removeCls){
15840             el.removeClass(removeCls);
15841             removeCls = null;
15842         }
15843     };
15844     
15845     return {
15846         /**
15847         * @cfg {Number} minWidth
15848         * The minimum width of the quick tip (defaults to 40)
15849         */
15850        minWidth : 40,
15851         /**
15852         * @cfg {Number} maxWidth
15853         * The maximum width of the quick tip (defaults to 300)
15854         */
15855        maxWidth : 300,
15856         /**
15857         * @cfg {Boolean} interceptTitles
15858         * True to automatically use the element's DOM title value if available (defaults to false)
15859         */
15860        interceptTitles : false,
15861         /**
15862         * @cfg {Boolean} trackMouse
15863         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15864         */
15865        trackMouse : false,
15866         /**
15867         * @cfg {Boolean} hideOnClick
15868         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15869         */
15870        hideOnClick : true,
15871         /**
15872         * @cfg {Number} showDelay
15873         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15874         */
15875        showDelay : 500,
15876         /**
15877         * @cfg {Number} hideDelay
15878         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15879         */
15880        hideDelay : 200,
15881         /**
15882         * @cfg {Boolean} autoHide
15883         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15884         * Used in conjunction with hideDelay.
15885         */
15886        autoHide : true,
15887         /**
15888         * @cfg {Boolean}
15889         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15890         * (defaults to true).  Used in conjunction with autoDismissDelay.
15891         */
15892        autoDismiss : true,
15893         /**
15894         * @cfg {Number}
15895         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15896         */
15897        autoDismissDelay : 5000,
15898        /**
15899         * @cfg {Boolean} animate
15900         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15901         */
15902        animate : false,
15903
15904        /**
15905         * @cfg {String} title
15906         * Title text to display (defaults to '').  This can be any valid HTML markup.
15907         */
15908         title: '',
15909        /**
15910         * @cfg {String} text
15911         * Body text to display (defaults to '').  This can be any valid HTML markup.
15912         */
15913         text : '',
15914        /**
15915         * @cfg {String} cls
15916         * A CSS class to apply to the base quick tip element (defaults to '').
15917         */
15918         cls : '',
15919        /**
15920         * @cfg {Number} width
15921         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15922         * minWidth or maxWidth.
15923         */
15924         width : null,
15925
15926     /**
15927      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15928      * or display QuickTips in a page.
15929      */
15930        init : function(){
15931           tm = Roo.QuickTips;
15932           cfg = tm.tagConfig;
15933           if(!inited){
15934               if(!Roo.isReady){ // allow calling of init() before onReady
15935                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15936                   return;
15937               }
15938               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15939               el.fxDefaults = {stopFx: true};
15940               // maximum custom styling
15941               //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>');
15942               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>');              
15943               tipTitle = el.child('h3');
15944               tipTitle.enableDisplayMode("block");
15945               tipBody = el.child('div.x-tip-bd');
15946               tipBodyText = el.child('div.x-tip-bd-inner');
15947               //bdLeft = el.child('div.x-tip-bd-left');
15948               //bdRight = el.child('div.x-tip-bd-right');
15949               close = el.child('div.x-tip-close');
15950               close.enableDisplayMode("block");
15951               close.on("click", hide);
15952               var d = Roo.get(document);
15953               d.on("mousedown", onDown);
15954               d.on("mouseover", onOver);
15955               d.on("mouseout", onOut);
15956               d.on("mousemove", onMove);
15957               esc = d.addKeyListener(27, hide);
15958               esc.disable();
15959               if(Roo.dd.DD){
15960                   dd = el.initDD("default", null, {
15961                       onDrag : function(){
15962                           el.sync();  
15963                       }
15964                   });
15965                   dd.setHandleElId(tipTitle.id);
15966                   dd.lock();
15967               }
15968               inited = true;
15969           }
15970           this.enable(); 
15971        },
15972
15973     /**
15974      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15975      * are supported:
15976      * <pre>
15977 Property    Type                   Description
15978 ----------  ---------------------  ------------------------------------------------------------------------
15979 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15980      * </ul>
15981      * @param {Object} config The config object
15982      */
15983        register : function(config){
15984            var cs = config instanceof Array ? config : arguments;
15985            for(var i = 0, len = cs.length; i < len; i++) {
15986                var c = cs[i];
15987                var target = c.target;
15988                if(target){
15989                    if(target instanceof Array){
15990                        for(var j = 0, jlen = target.length; j < jlen; j++){
15991                            tagEls[target[j]] = c;
15992                        }
15993                    }else{
15994                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15995                    }
15996                }
15997            }
15998        },
15999
16000     /**
16001      * Removes this quick tip from its element and destroys it.
16002      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16003      */
16004        unregister : function(el){
16005            delete tagEls[Roo.id(el)];
16006        },
16007
16008     /**
16009      * Enable this quick tip.
16010      */
16011        enable : function(){
16012            if(inited && disabled){
16013                locks.pop();
16014                if(locks.length < 1){
16015                    disabled = false;
16016                }
16017            }
16018        },
16019
16020     /**
16021      * Disable this quick tip.
16022      */
16023        disable : function(){
16024           disabled = true;
16025           clearTimeout(showProc);
16026           clearTimeout(hideProc);
16027           clearTimeout(dismissProc);
16028           if(ce){
16029               hide(true);
16030           }
16031           locks.push(1);
16032        },
16033
16034     /**
16035      * Returns true if the quick tip is enabled, else false.
16036      */
16037        isEnabled : function(){
16038             return !disabled;
16039        },
16040
16041         // private
16042        tagConfig : {
16043            namespace : "ext",
16044            attribute : "qtip",
16045            width : "width",
16046            target : "target",
16047            title : "qtitle",
16048            hide : "hide",
16049            cls : "qclass"
16050        }
16051    };
16052 }();
16053
16054 // backwards compat
16055 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16056  * Based on:
16057  * Ext JS Library 1.1.1
16058  * Copyright(c) 2006-2007, Ext JS, LLC.
16059  *
16060  * Originally Released Under LGPL - original licence link has changed is not relivant.
16061  *
16062  * Fork - LGPL
16063  * <script type="text/javascript">
16064  */
16065  
16066
16067 /**
16068  * @class Roo.tree.TreePanel
16069  * @extends Roo.data.Tree
16070
16071  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16072  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16073  * @cfg {Boolean} enableDD true to enable drag and drop
16074  * @cfg {Boolean} enableDrag true to enable just drag
16075  * @cfg {Boolean} enableDrop true to enable just drop
16076  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16077  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16078  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16079  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16080  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16081  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16082  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16083  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16084  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16085  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16086  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16087  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16088  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16089  * @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>
16090  * @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>
16091  * 
16092  * @constructor
16093  * @param {String/HTMLElement/Element} el The container element
16094  * @param {Object} config
16095  */
16096 Roo.tree.TreePanel = function(el, config){
16097     var root = false;
16098     var loader = false;
16099     if (config.root) {
16100         root = config.root;
16101         delete config.root;
16102     }
16103     if (config.loader) {
16104         loader = config.loader;
16105         delete config.loader;
16106     }
16107     
16108     Roo.apply(this, config);
16109     Roo.tree.TreePanel.superclass.constructor.call(this);
16110     this.el = Roo.get(el);
16111     this.el.addClass('x-tree');
16112     //console.log(root);
16113     if (root) {
16114         this.setRootNode( Roo.factory(root, Roo.tree));
16115     }
16116     if (loader) {
16117         this.loader = Roo.factory(loader, Roo.tree);
16118     }
16119    /**
16120     * Read-only. The id of the container element becomes this TreePanel's id.
16121     */
16122    this.id = this.el.id;
16123    this.addEvents({
16124         /**
16125         * @event beforeload
16126         * Fires before a node is loaded, return false to cancel
16127         * @param {Node} node The node being loaded
16128         */
16129         "beforeload" : true,
16130         /**
16131         * @event load
16132         * Fires when a node is loaded
16133         * @param {Node} node The node that was loaded
16134         */
16135         "load" : true,
16136         /**
16137         * @event textchange
16138         * Fires when the text for a node is changed
16139         * @param {Node} node The node
16140         * @param {String} text The new text
16141         * @param {String} oldText The old text
16142         */
16143         "textchange" : true,
16144         /**
16145         * @event beforeexpand
16146         * Fires before a node is expanded, return false to cancel.
16147         * @param {Node} node The node
16148         * @param {Boolean} deep
16149         * @param {Boolean} anim
16150         */
16151         "beforeexpand" : true,
16152         /**
16153         * @event beforecollapse
16154         * Fires before a node is collapsed, return false to cancel.
16155         * @param {Node} node The node
16156         * @param {Boolean} deep
16157         * @param {Boolean} anim
16158         */
16159         "beforecollapse" : true,
16160         /**
16161         * @event expand
16162         * Fires when a node is expanded
16163         * @param {Node} node The node
16164         */
16165         "expand" : true,
16166         /**
16167         * @event disabledchange
16168         * Fires when the disabled status of a node changes
16169         * @param {Node} node The node
16170         * @param {Boolean} disabled
16171         */
16172         "disabledchange" : true,
16173         /**
16174         * @event collapse
16175         * Fires when a node is collapsed
16176         * @param {Node} node The node
16177         */
16178         "collapse" : true,
16179         /**
16180         * @event beforeclick
16181         * Fires before click processing on a node. Return false to cancel the default action.
16182         * @param {Node} node The node
16183         * @param {Roo.EventObject} e The event object
16184         */
16185         "beforeclick":true,
16186         /**
16187         * @event checkchange
16188         * Fires when a node with a checkbox's checked property changes
16189         * @param {Node} this This node
16190         * @param {Boolean} checked
16191         */
16192         "checkchange":true,
16193         /**
16194         * @event click
16195         * Fires when a node is clicked
16196         * @param {Node} node The node
16197         * @param {Roo.EventObject} e The event object
16198         */
16199         "click":true,
16200         /**
16201         * @event dblclick
16202         * Fires when a node is double clicked
16203         * @param {Node} node The node
16204         * @param {Roo.EventObject} e The event object
16205         */
16206         "dblclick":true,
16207         /**
16208         * @event contextmenu
16209         * Fires when a node is right clicked
16210         * @param {Node} node The node
16211         * @param {Roo.EventObject} e The event object
16212         */
16213         "contextmenu":true,
16214         /**
16215         * @event beforechildrenrendered
16216         * Fires right before the child nodes for a node are rendered
16217         * @param {Node} node The node
16218         */
16219         "beforechildrenrendered":true,
16220        /**
16221              * @event startdrag
16222              * Fires when a node starts being dragged
16223              * @param {Roo.tree.TreePanel} this
16224              * @param {Roo.tree.TreeNode} node
16225              * @param {event} e The raw browser event
16226              */ 
16227             "startdrag" : true,
16228             /**
16229              * @event enddrag
16230              * Fires when a drag operation is complete
16231              * @param {Roo.tree.TreePanel} this
16232              * @param {Roo.tree.TreeNode} node
16233              * @param {event} e The raw browser event
16234              */
16235             "enddrag" : true,
16236             /**
16237              * @event dragdrop
16238              * Fires when a dragged node is dropped on a valid DD target
16239              * @param {Roo.tree.TreePanel} this
16240              * @param {Roo.tree.TreeNode} node
16241              * @param {DD} dd The dd it was dropped on
16242              * @param {event} e The raw browser event
16243              */
16244             "dragdrop" : true,
16245             /**
16246              * @event beforenodedrop
16247              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16248              * passed to handlers has the following properties:<br />
16249              * <ul style="padding:5px;padding-left:16px;">
16250              * <li>tree - The TreePanel</li>
16251              * <li>target - The node being targeted for the drop</li>
16252              * <li>data - The drag data from the drag source</li>
16253              * <li>point - The point of the drop - append, above or below</li>
16254              * <li>source - The drag source</li>
16255              * <li>rawEvent - Raw mouse event</li>
16256              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16257              * to be inserted by setting them on this object.</li>
16258              * <li>cancel - Set this to true to cancel the drop.</li>
16259              * </ul>
16260              * @param {Object} dropEvent
16261              */
16262             "beforenodedrop" : true,
16263             /**
16264              * @event nodedrop
16265              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16266              * passed to handlers has the following properties:<br />
16267              * <ul style="padding:5px;padding-left:16px;">
16268              * <li>tree - The TreePanel</li>
16269              * <li>target - The node being targeted for the drop</li>
16270              * <li>data - The drag data from the drag source</li>
16271              * <li>point - The point of the drop - append, above or below</li>
16272              * <li>source - The drag source</li>
16273              * <li>rawEvent - Raw mouse event</li>
16274              * <li>dropNode - Dropped node(s).</li>
16275              * </ul>
16276              * @param {Object} dropEvent
16277              */
16278             "nodedrop" : true,
16279              /**
16280              * @event nodedragover
16281              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16282              * passed to handlers has the following properties:<br />
16283              * <ul style="padding:5px;padding-left:16px;">
16284              * <li>tree - The TreePanel</li>
16285              * <li>target - The node being targeted for the drop</li>
16286              * <li>data - The drag data from the drag source</li>
16287              * <li>point - The point of the drop - append, above or below</li>
16288              * <li>source - The drag source</li>
16289              * <li>rawEvent - Raw mouse event</li>
16290              * <li>dropNode - Drop node(s) provided by the source.</li>
16291              * <li>cancel - Set this to true to signal drop not allowed.</li>
16292              * </ul>
16293              * @param {Object} dragOverEvent
16294              */
16295             "nodedragover" : true
16296         
16297    });
16298    if(this.singleExpand){
16299        this.on("beforeexpand", this.restrictExpand, this);
16300    }
16301 };
16302 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16303     rootVisible : true,
16304     animate: Roo.enableFx,
16305     lines : true,
16306     enableDD : false,
16307     hlDrop : Roo.enableFx,
16308   
16309     renderer: false,
16310     
16311     rendererTip: false,
16312     // private
16313     restrictExpand : function(node){
16314         var p = node.parentNode;
16315         if(p){
16316             if(p.expandedChild && p.expandedChild.parentNode == p){
16317                 p.expandedChild.collapse();
16318             }
16319             p.expandedChild = node;
16320         }
16321     },
16322
16323     // private override
16324     setRootNode : function(node){
16325         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16326         if(!this.rootVisible){
16327             node.ui = new Roo.tree.RootTreeNodeUI(node);
16328         }
16329         return node;
16330     },
16331
16332     /**
16333      * Returns the container element for this TreePanel
16334      */
16335     getEl : function(){
16336         return this.el;
16337     },
16338
16339     /**
16340      * Returns the default TreeLoader for this TreePanel
16341      */
16342     getLoader : function(){
16343         return this.loader;
16344     },
16345
16346     /**
16347      * Expand all nodes
16348      */
16349     expandAll : function(){
16350         this.root.expand(true);
16351     },
16352
16353     /**
16354      * Collapse all nodes
16355      */
16356     collapseAll : function(){
16357         this.root.collapse(true);
16358     },
16359
16360     /**
16361      * Returns the selection model used by this TreePanel
16362      */
16363     getSelectionModel : function(){
16364         if(!this.selModel){
16365             this.selModel = new Roo.tree.DefaultSelectionModel();
16366         }
16367         return this.selModel;
16368     },
16369
16370     /**
16371      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16372      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16373      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16374      * @return {Array}
16375      */
16376     getChecked : function(a, startNode){
16377         startNode = startNode || this.root;
16378         var r = [];
16379         var f = function(){
16380             if(this.attributes.checked){
16381                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16382             }
16383         }
16384         startNode.cascade(f);
16385         return r;
16386     },
16387
16388     /**
16389      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16390      * @param {String} path
16391      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16392      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16393      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16394      */
16395     expandPath : function(path, attr, callback){
16396         attr = attr || "id";
16397         var keys = path.split(this.pathSeparator);
16398         var curNode = this.root;
16399         if(curNode.attributes[attr] != keys[1]){ // invalid root
16400             if(callback){
16401                 callback(false, null);
16402             }
16403             return;
16404         }
16405         var index = 1;
16406         var f = function(){
16407             if(++index == keys.length){
16408                 if(callback){
16409                     callback(true, curNode);
16410                 }
16411                 return;
16412             }
16413             var c = curNode.findChild(attr, keys[index]);
16414             if(!c){
16415                 if(callback){
16416                     callback(false, curNode);
16417                 }
16418                 return;
16419             }
16420             curNode = c;
16421             c.expand(false, false, f);
16422         };
16423         curNode.expand(false, false, f);
16424     },
16425
16426     /**
16427      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16428      * @param {String} path
16429      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16430      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16431      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16432      */
16433     selectPath : function(path, attr, callback){
16434         attr = attr || "id";
16435         var keys = path.split(this.pathSeparator);
16436         var v = keys.pop();
16437         if(keys.length > 0){
16438             var f = function(success, node){
16439                 if(success && node){
16440                     var n = node.findChild(attr, v);
16441                     if(n){
16442                         n.select();
16443                         if(callback){
16444                             callback(true, n);
16445                         }
16446                     }else if(callback){
16447                         callback(false, n);
16448                     }
16449                 }else{
16450                     if(callback){
16451                         callback(false, n);
16452                     }
16453                 }
16454             };
16455             this.expandPath(keys.join(this.pathSeparator), attr, f);
16456         }else{
16457             this.root.select();
16458             if(callback){
16459                 callback(true, this.root);
16460             }
16461         }
16462     },
16463
16464     getTreeEl : function(){
16465         return this.el;
16466     },
16467
16468     /**
16469      * Trigger rendering of this TreePanel
16470      */
16471     render : function(){
16472         if (this.innerCt) {
16473             return this; // stop it rendering more than once!!
16474         }
16475         
16476         this.innerCt = this.el.createChild({tag:"ul",
16477                cls:"x-tree-root-ct " +
16478                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16479
16480         if(this.containerScroll){
16481             Roo.dd.ScrollManager.register(this.el);
16482         }
16483         if((this.enableDD || this.enableDrop) && !this.dropZone){
16484            /**
16485             * The dropZone used by this tree if drop is enabled
16486             * @type Roo.tree.TreeDropZone
16487             */
16488              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16489                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16490            });
16491         }
16492         if((this.enableDD || this.enableDrag) && !this.dragZone){
16493            /**
16494             * The dragZone used by this tree if drag is enabled
16495             * @type Roo.tree.TreeDragZone
16496             */
16497             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16498                ddGroup: this.ddGroup || "TreeDD",
16499                scroll: this.ddScroll
16500            });
16501         }
16502         this.getSelectionModel().init(this);
16503         if (!this.root) {
16504             console.log("ROOT not set in tree");
16505             return;
16506         }
16507         this.root.render();
16508         if(!this.rootVisible){
16509             this.root.renderChildren();
16510         }
16511         return this;
16512     }
16513 });/*
16514  * Based on:
16515  * Ext JS Library 1.1.1
16516  * Copyright(c) 2006-2007, Ext JS, LLC.
16517  *
16518  * Originally Released Under LGPL - original licence link has changed is not relivant.
16519  *
16520  * Fork - LGPL
16521  * <script type="text/javascript">
16522  */
16523  
16524
16525 /**
16526  * @class Roo.tree.DefaultSelectionModel
16527  * @extends Roo.util.Observable
16528  * The default single selection for a TreePanel.
16529  */
16530 Roo.tree.DefaultSelectionModel = function(){
16531    this.selNode = null;
16532    
16533    this.addEvents({
16534        /**
16535         * @event selectionchange
16536         * Fires when the selected node changes
16537         * @param {DefaultSelectionModel} this
16538         * @param {TreeNode} node the new selection
16539         */
16540        "selectionchange" : true,
16541
16542        /**
16543         * @event beforeselect
16544         * Fires before the selected node changes, return false to cancel the change
16545         * @param {DefaultSelectionModel} this
16546         * @param {TreeNode} node the new selection
16547         * @param {TreeNode} node the old selection
16548         */
16549        "beforeselect" : true
16550    });
16551 };
16552
16553 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16554     init : function(tree){
16555         this.tree = tree;
16556         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16557         tree.on("click", this.onNodeClick, this);
16558     },
16559     
16560     onNodeClick : function(node, e){
16561         if (e.ctrlKey && this.selNode == node)  {
16562             this.unselect(node);
16563             return;
16564         }
16565         this.select(node);
16566     },
16567     
16568     /**
16569      * Select a node.
16570      * @param {TreeNode} node The node to select
16571      * @return {TreeNode} The selected node
16572      */
16573     select : function(node){
16574         var last = this.selNode;
16575         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16576             if(last){
16577                 last.ui.onSelectedChange(false);
16578             }
16579             this.selNode = node;
16580             node.ui.onSelectedChange(true);
16581             this.fireEvent("selectionchange", this, node, last);
16582         }
16583         return node;
16584     },
16585     
16586     /**
16587      * Deselect a node.
16588      * @param {TreeNode} node The node to unselect
16589      */
16590     unselect : function(node){
16591         if(this.selNode == node){
16592             this.clearSelections();
16593         }    
16594     },
16595     
16596     /**
16597      * Clear all selections
16598      */
16599     clearSelections : function(){
16600         var n = this.selNode;
16601         if(n){
16602             n.ui.onSelectedChange(false);
16603             this.selNode = null;
16604             this.fireEvent("selectionchange", this, null);
16605         }
16606         return n;
16607     },
16608     
16609     /**
16610      * Get the selected node
16611      * @return {TreeNode} The selected node
16612      */
16613     getSelectedNode : function(){
16614         return this.selNode;    
16615     },
16616     
16617     /**
16618      * Returns true if the node is selected
16619      * @param {TreeNode} node The node to check
16620      * @return {Boolean}
16621      */
16622     isSelected : function(node){
16623         return this.selNode == node;  
16624     },
16625
16626     /**
16627      * Selects the node above the selected node in the tree, intelligently walking the nodes
16628      * @return TreeNode The new selection
16629      */
16630     selectPrevious : function(){
16631         var s = this.selNode || this.lastSelNode;
16632         if(!s){
16633             return null;
16634         }
16635         var ps = s.previousSibling;
16636         if(ps){
16637             if(!ps.isExpanded() || ps.childNodes.length < 1){
16638                 return this.select(ps);
16639             } else{
16640                 var lc = ps.lastChild;
16641                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16642                     lc = lc.lastChild;
16643                 }
16644                 return this.select(lc);
16645             }
16646         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16647             return this.select(s.parentNode);
16648         }
16649         return null;
16650     },
16651
16652     /**
16653      * Selects the node above the selected node in the tree, intelligently walking the nodes
16654      * @return TreeNode The new selection
16655      */
16656     selectNext : function(){
16657         var s = this.selNode || this.lastSelNode;
16658         if(!s){
16659             return null;
16660         }
16661         if(s.firstChild && s.isExpanded()){
16662              return this.select(s.firstChild);
16663          }else if(s.nextSibling){
16664              return this.select(s.nextSibling);
16665          }else if(s.parentNode){
16666             var newS = null;
16667             s.parentNode.bubble(function(){
16668                 if(this.nextSibling){
16669                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16670                     return false;
16671                 }
16672             });
16673             return newS;
16674          }
16675         return null;
16676     },
16677
16678     onKeyDown : function(e){
16679         var s = this.selNode || this.lastSelNode;
16680         // undesirable, but required
16681         var sm = this;
16682         if(!s){
16683             return;
16684         }
16685         var k = e.getKey();
16686         switch(k){
16687              case e.DOWN:
16688                  e.stopEvent();
16689                  this.selectNext();
16690              break;
16691              case e.UP:
16692                  e.stopEvent();
16693                  this.selectPrevious();
16694              break;
16695              case e.RIGHT:
16696                  e.preventDefault();
16697                  if(s.hasChildNodes()){
16698                      if(!s.isExpanded()){
16699                          s.expand();
16700                      }else if(s.firstChild){
16701                          this.select(s.firstChild, e);
16702                      }
16703                  }
16704              break;
16705              case e.LEFT:
16706                  e.preventDefault();
16707                  if(s.hasChildNodes() && s.isExpanded()){
16708                      s.collapse();
16709                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16710                      this.select(s.parentNode, e);
16711                  }
16712              break;
16713         };
16714     }
16715 });
16716
16717 /**
16718  * @class Roo.tree.MultiSelectionModel
16719  * @extends Roo.util.Observable
16720  * Multi selection for a TreePanel.
16721  */
16722 Roo.tree.MultiSelectionModel = function(){
16723    this.selNodes = [];
16724    this.selMap = {};
16725    this.addEvents({
16726        /**
16727         * @event selectionchange
16728         * Fires when the selected nodes change
16729         * @param {MultiSelectionModel} this
16730         * @param {Array} nodes Array of the selected nodes
16731         */
16732        "selectionchange" : true
16733    });
16734 };
16735
16736 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16737     init : function(tree){
16738         this.tree = tree;
16739         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16740         tree.on("click", this.onNodeClick, this);
16741     },
16742     
16743     onNodeClick : function(node, e){
16744         this.select(node, e, e.ctrlKey);
16745     },
16746     
16747     /**
16748      * Select a node.
16749      * @param {TreeNode} node The node to select
16750      * @param {EventObject} e (optional) An event associated with the selection
16751      * @param {Boolean} keepExisting True to retain existing selections
16752      * @return {TreeNode} The selected node
16753      */
16754     select : function(node, e, keepExisting){
16755         if(keepExisting !== true){
16756             this.clearSelections(true);
16757         }
16758         if(this.isSelected(node)){
16759             this.lastSelNode = node;
16760             return node;
16761         }
16762         this.selNodes.push(node);
16763         this.selMap[node.id] = node;
16764         this.lastSelNode = node;
16765         node.ui.onSelectedChange(true);
16766         this.fireEvent("selectionchange", this, this.selNodes);
16767         return node;
16768     },
16769     
16770     /**
16771      * Deselect a node.
16772      * @param {TreeNode} node The node to unselect
16773      */
16774     unselect : function(node){
16775         if(this.selMap[node.id]){
16776             node.ui.onSelectedChange(false);
16777             var sn = this.selNodes;
16778             var index = -1;
16779             if(sn.indexOf){
16780                 index = sn.indexOf(node);
16781             }else{
16782                 for(var i = 0, len = sn.length; i < len; i++){
16783                     if(sn[i] == node){
16784                         index = i;
16785                         break;
16786                     }
16787                 }
16788             }
16789             if(index != -1){
16790                 this.selNodes.splice(index, 1);
16791             }
16792             delete this.selMap[node.id];
16793             this.fireEvent("selectionchange", this, this.selNodes);
16794         }
16795     },
16796     
16797     /**
16798      * Clear all selections
16799      */
16800     clearSelections : function(suppressEvent){
16801         var sn = this.selNodes;
16802         if(sn.length > 0){
16803             for(var i = 0, len = sn.length; i < len; i++){
16804                 sn[i].ui.onSelectedChange(false);
16805             }
16806             this.selNodes = [];
16807             this.selMap = {};
16808             if(suppressEvent !== true){
16809                 this.fireEvent("selectionchange", this, this.selNodes);
16810             }
16811         }
16812     },
16813     
16814     /**
16815      * Returns true if the node is selected
16816      * @param {TreeNode} node The node to check
16817      * @return {Boolean}
16818      */
16819     isSelected : function(node){
16820         return this.selMap[node.id] ? true : false;  
16821     },
16822     
16823     /**
16824      * Returns an array of the selected nodes
16825      * @return {Array}
16826      */
16827     getSelectedNodes : function(){
16828         return this.selNodes;    
16829     },
16830
16831     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16832
16833     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16834
16835     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16836 });/*
16837  * Based on:
16838  * Ext JS Library 1.1.1
16839  * Copyright(c) 2006-2007, Ext JS, LLC.
16840  *
16841  * Originally Released Under LGPL - original licence link has changed is not relivant.
16842  *
16843  * Fork - LGPL
16844  * <script type="text/javascript">
16845  */
16846  
16847 /**
16848  * @class Roo.tree.TreeNode
16849  * @extends Roo.data.Node
16850  * @cfg {String} text The text for this node
16851  * @cfg {Boolean} expanded true to start the node expanded
16852  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16853  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16854  * @cfg {Boolean} disabled true to start the node disabled
16855  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16856  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16857  * @cfg {String} cls A css class to be added to the node
16858  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16859  * @cfg {String} href URL of the link used for the node (defaults to #)
16860  * @cfg {String} hrefTarget target frame for the link
16861  * @cfg {String} qtip An Ext QuickTip for the node
16862  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16863  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16864  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16865  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16866  * (defaults to undefined with no checkbox rendered)
16867  * @constructor
16868  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16869  */
16870 Roo.tree.TreeNode = function(attributes){
16871     attributes = attributes || {};
16872     if(typeof attributes == "string"){
16873         attributes = {text: attributes};
16874     }
16875     this.childrenRendered = false;
16876     this.rendered = false;
16877     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16878     this.expanded = attributes.expanded === true;
16879     this.isTarget = attributes.isTarget !== false;
16880     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16881     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16882
16883     /**
16884      * Read-only. The text for this node. To change it use setText().
16885      * @type String
16886      */
16887     this.text = attributes.text;
16888     /**
16889      * True if this node is disabled.
16890      * @type Boolean
16891      */
16892     this.disabled = attributes.disabled === true;
16893
16894     this.addEvents({
16895         /**
16896         * @event textchange
16897         * Fires when the text for this node is changed
16898         * @param {Node} this This node
16899         * @param {String} text The new text
16900         * @param {String} oldText The old text
16901         */
16902         "textchange" : true,
16903         /**
16904         * @event beforeexpand
16905         * Fires before this node is expanded, return false to cancel.
16906         * @param {Node} this This node
16907         * @param {Boolean} deep
16908         * @param {Boolean} anim
16909         */
16910         "beforeexpand" : true,
16911         /**
16912         * @event beforecollapse
16913         * Fires before this node is collapsed, return false to cancel.
16914         * @param {Node} this This node
16915         * @param {Boolean} deep
16916         * @param {Boolean} anim
16917         */
16918         "beforecollapse" : true,
16919         /**
16920         * @event expand
16921         * Fires when this node is expanded
16922         * @param {Node} this This node
16923         */
16924         "expand" : true,
16925         /**
16926         * @event disabledchange
16927         * Fires when the disabled status of this node changes
16928         * @param {Node} this This node
16929         * @param {Boolean} disabled
16930         */
16931         "disabledchange" : true,
16932         /**
16933         * @event collapse
16934         * Fires when this node is collapsed
16935         * @param {Node} this This node
16936         */
16937         "collapse" : true,
16938         /**
16939         * @event beforeclick
16940         * Fires before click processing. Return false to cancel the default action.
16941         * @param {Node} this This node
16942         * @param {Roo.EventObject} e The event object
16943         */
16944         "beforeclick":true,
16945         /**
16946         * @event checkchange
16947         * Fires when a node with a checkbox's checked property changes
16948         * @param {Node} this This node
16949         * @param {Boolean} checked
16950         */
16951         "checkchange":true,
16952         /**
16953         * @event click
16954         * Fires when this node is clicked
16955         * @param {Node} this This node
16956         * @param {Roo.EventObject} e The event object
16957         */
16958         "click":true,
16959         /**
16960         * @event dblclick
16961         * Fires when this node is double clicked
16962         * @param {Node} this This node
16963         * @param {Roo.EventObject} e The event object
16964         */
16965         "dblclick":true,
16966         /**
16967         * @event contextmenu
16968         * Fires when this node is right clicked
16969         * @param {Node} this This node
16970         * @param {Roo.EventObject} e The event object
16971         */
16972         "contextmenu":true,
16973         /**
16974         * @event beforechildrenrendered
16975         * Fires right before the child nodes for this node are rendered
16976         * @param {Node} this This node
16977         */
16978         "beforechildrenrendered":true
16979     });
16980
16981     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16982
16983     /**
16984      * Read-only. The UI for this node
16985      * @type TreeNodeUI
16986      */
16987     this.ui = new uiClass(this);
16988 };
16989 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16990     preventHScroll: true,
16991     /**
16992      * Returns true if this node is expanded
16993      * @return {Boolean}
16994      */
16995     isExpanded : function(){
16996         return this.expanded;
16997     },
16998
16999     /**
17000      * Returns the UI object for this node
17001      * @return {TreeNodeUI}
17002      */
17003     getUI : function(){
17004         return this.ui;
17005     },
17006
17007     // private override
17008     setFirstChild : function(node){
17009         var of = this.firstChild;
17010         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17011         if(this.childrenRendered && of && node != of){
17012             of.renderIndent(true, true);
17013         }
17014         if(this.rendered){
17015             this.renderIndent(true, true);
17016         }
17017     },
17018
17019     // private override
17020     setLastChild : function(node){
17021         var ol = this.lastChild;
17022         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17023         if(this.childrenRendered && ol && node != ol){
17024             ol.renderIndent(true, true);
17025         }
17026         if(this.rendered){
17027             this.renderIndent(true, true);
17028         }
17029     },
17030
17031     // these methods are overridden to provide lazy rendering support
17032     // private override
17033     appendChild : function(){
17034         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17035         if(node && this.childrenRendered){
17036             node.render();
17037         }
17038         this.ui.updateExpandIcon();
17039         return node;
17040     },
17041
17042     // private override
17043     removeChild : function(node){
17044         this.ownerTree.getSelectionModel().unselect(node);
17045         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17046         // if it's been rendered remove dom node
17047         if(this.childrenRendered){
17048             node.ui.remove();
17049         }
17050         if(this.childNodes.length < 1){
17051             this.collapse(false, false);
17052         }else{
17053             this.ui.updateExpandIcon();
17054         }
17055         if(!this.firstChild) {
17056             this.childrenRendered = false;
17057         }
17058         return node;
17059     },
17060
17061     // private override
17062     insertBefore : function(node, refNode){
17063         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17064         if(newNode && refNode && this.childrenRendered){
17065             node.render();
17066         }
17067         this.ui.updateExpandIcon();
17068         return newNode;
17069     },
17070
17071     /**
17072      * Sets the text for this node
17073      * @param {String} text
17074      */
17075     setText : function(text){
17076         var oldText = this.text;
17077         this.text = text;
17078         this.attributes.text = text;
17079         if(this.rendered){ // event without subscribing
17080             this.ui.onTextChange(this, text, oldText);
17081         }
17082         this.fireEvent("textchange", this, text, oldText);
17083     },
17084
17085     /**
17086      * Triggers selection of this node
17087      */
17088     select : function(){
17089         this.getOwnerTree().getSelectionModel().select(this);
17090     },
17091
17092     /**
17093      * Triggers deselection of this node
17094      */
17095     unselect : function(){
17096         this.getOwnerTree().getSelectionModel().unselect(this);
17097     },
17098
17099     /**
17100      * Returns true if this node is selected
17101      * @return {Boolean}
17102      */
17103     isSelected : function(){
17104         return this.getOwnerTree().getSelectionModel().isSelected(this);
17105     },
17106
17107     /**
17108      * Expand this node.
17109      * @param {Boolean} deep (optional) True to expand all children as well
17110      * @param {Boolean} anim (optional) false to cancel the default animation
17111      * @param {Function} callback (optional) A callback to be called when
17112      * expanding this node completes (does not wait for deep expand to complete).
17113      * Called with 1 parameter, this node.
17114      */
17115     expand : function(deep, anim, callback){
17116         if(!this.expanded){
17117             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17118                 return;
17119             }
17120             if(!this.childrenRendered){
17121                 this.renderChildren();
17122             }
17123             this.expanded = true;
17124             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17125                 this.ui.animExpand(function(){
17126                     this.fireEvent("expand", this);
17127                     if(typeof callback == "function"){
17128                         callback(this);
17129                     }
17130                     if(deep === true){
17131                         this.expandChildNodes(true);
17132                     }
17133                 }.createDelegate(this));
17134                 return;
17135             }else{
17136                 this.ui.expand();
17137                 this.fireEvent("expand", this);
17138                 if(typeof callback == "function"){
17139                     callback(this);
17140                 }
17141             }
17142         }else{
17143            if(typeof callback == "function"){
17144                callback(this);
17145            }
17146         }
17147         if(deep === true){
17148             this.expandChildNodes(true);
17149         }
17150     },
17151
17152     isHiddenRoot : function(){
17153         return this.isRoot && !this.getOwnerTree().rootVisible;
17154     },
17155
17156     /**
17157      * Collapse this node.
17158      * @param {Boolean} deep (optional) True to collapse all children as well
17159      * @param {Boolean} anim (optional) false to cancel the default animation
17160      */
17161     collapse : function(deep, anim){
17162         if(this.expanded && !this.isHiddenRoot()){
17163             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17164                 return;
17165             }
17166             this.expanded = false;
17167             if((this.getOwnerTree().animate && anim !== false) || anim){
17168                 this.ui.animCollapse(function(){
17169                     this.fireEvent("collapse", this);
17170                     if(deep === true){
17171                         this.collapseChildNodes(true);
17172                     }
17173                 }.createDelegate(this));
17174                 return;
17175             }else{
17176                 this.ui.collapse();
17177                 this.fireEvent("collapse", this);
17178             }
17179         }
17180         if(deep === true){
17181             var cs = this.childNodes;
17182             for(var i = 0, len = cs.length; i < len; i++) {
17183                 cs[i].collapse(true, false);
17184             }
17185         }
17186     },
17187
17188     // private
17189     delayedExpand : function(delay){
17190         if(!this.expandProcId){
17191             this.expandProcId = this.expand.defer(delay, this);
17192         }
17193     },
17194
17195     // private
17196     cancelExpand : function(){
17197         if(this.expandProcId){
17198             clearTimeout(this.expandProcId);
17199         }
17200         this.expandProcId = false;
17201     },
17202
17203     /**
17204      * Toggles expanded/collapsed state of the node
17205      */
17206     toggle : function(){
17207         if(this.expanded){
17208             this.collapse();
17209         }else{
17210             this.expand();
17211         }
17212     },
17213
17214     /**
17215      * Ensures all parent nodes are expanded
17216      */
17217     ensureVisible : function(callback){
17218         var tree = this.getOwnerTree();
17219         tree.expandPath(this.parentNode.getPath(), false, function(){
17220             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17221             Roo.callback(callback);
17222         }.createDelegate(this));
17223     },
17224
17225     /**
17226      * Expand all child nodes
17227      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17228      */
17229     expandChildNodes : function(deep){
17230         var cs = this.childNodes;
17231         for(var i = 0, len = cs.length; i < len; i++) {
17232                 cs[i].expand(deep);
17233         }
17234     },
17235
17236     /**
17237      * Collapse all child nodes
17238      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17239      */
17240     collapseChildNodes : function(deep){
17241         var cs = this.childNodes;
17242         for(var i = 0, len = cs.length; i < len; i++) {
17243                 cs[i].collapse(deep);
17244         }
17245     },
17246
17247     /**
17248      * Disables this node
17249      */
17250     disable : function(){
17251         this.disabled = true;
17252         this.unselect();
17253         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17254             this.ui.onDisableChange(this, true);
17255         }
17256         this.fireEvent("disabledchange", this, true);
17257     },
17258
17259     /**
17260      * Enables this node
17261      */
17262     enable : function(){
17263         this.disabled = false;
17264         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17265             this.ui.onDisableChange(this, false);
17266         }
17267         this.fireEvent("disabledchange", this, false);
17268     },
17269
17270     // private
17271     renderChildren : function(suppressEvent){
17272         if(suppressEvent !== false){
17273             this.fireEvent("beforechildrenrendered", this);
17274         }
17275         var cs = this.childNodes;
17276         for(var i = 0, len = cs.length; i < len; i++){
17277             cs[i].render(true);
17278         }
17279         this.childrenRendered = true;
17280     },
17281
17282     // private
17283     sort : function(fn, scope){
17284         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17285         if(this.childrenRendered){
17286             var cs = this.childNodes;
17287             for(var i = 0, len = cs.length; i < len; i++){
17288                 cs[i].render(true);
17289             }
17290         }
17291     },
17292
17293     // private
17294     render : function(bulkRender){
17295         this.ui.render(bulkRender);
17296         if(!this.rendered){
17297             this.rendered = true;
17298             if(this.expanded){
17299                 this.expanded = false;
17300                 this.expand(false, false);
17301             }
17302         }
17303     },
17304
17305     // private
17306     renderIndent : function(deep, refresh){
17307         if(refresh){
17308             this.ui.childIndent = null;
17309         }
17310         this.ui.renderIndent();
17311         if(deep === true && this.childrenRendered){
17312             var cs = this.childNodes;
17313             for(var i = 0, len = cs.length; i < len; i++){
17314                 cs[i].renderIndent(true, refresh);
17315             }
17316         }
17317     }
17318 });/*
17319  * Based on:
17320  * Ext JS Library 1.1.1
17321  * Copyright(c) 2006-2007, Ext JS, LLC.
17322  *
17323  * Originally Released Under LGPL - original licence link has changed is not relivant.
17324  *
17325  * Fork - LGPL
17326  * <script type="text/javascript">
17327  */
17328  
17329 /**
17330  * @class Roo.tree.AsyncTreeNode
17331  * @extends Roo.tree.TreeNode
17332  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17333  * @constructor
17334  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17335  */
17336  Roo.tree.AsyncTreeNode = function(config){
17337     this.loaded = false;
17338     this.loading = false;
17339     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17340     /**
17341     * @event beforeload
17342     * Fires before this node is loaded, return false to cancel
17343     * @param {Node} this This node
17344     */
17345     this.addEvents({'beforeload':true, 'load': true});
17346     /**
17347     * @event load
17348     * Fires when this node is loaded
17349     * @param {Node} this This node
17350     */
17351     /**
17352      * The loader used by this node (defaults to using the tree's defined loader)
17353      * @type TreeLoader
17354      * @property loader
17355      */
17356 };
17357 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17358     expand : function(deep, anim, callback){
17359         if(this.loading){ // if an async load is already running, waiting til it's done
17360             var timer;
17361             var f = function(){
17362                 if(!this.loading){ // done loading
17363                     clearInterval(timer);
17364                     this.expand(deep, anim, callback);
17365                 }
17366             }.createDelegate(this);
17367             timer = setInterval(f, 200);
17368             return;
17369         }
17370         if(!this.loaded){
17371             if(this.fireEvent("beforeload", this) === false){
17372                 return;
17373             }
17374             this.loading = true;
17375             this.ui.beforeLoad(this);
17376             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17377             if(loader){
17378                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17379                 return;
17380             }
17381         }
17382         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17383     },
17384     
17385     /**
17386      * Returns true if this node is currently loading
17387      * @return {Boolean}
17388      */
17389     isLoading : function(){
17390         return this.loading;  
17391     },
17392     
17393     loadComplete : function(deep, anim, callback){
17394         this.loading = false;
17395         this.loaded = true;
17396         this.ui.afterLoad(this);
17397         this.fireEvent("load", this);
17398         this.expand(deep, anim, callback);
17399     },
17400     
17401     /**
17402      * Returns true if this node has been loaded
17403      * @return {Boolean}
17404      */
17405     isLoaded : function(){
17406         return this.loaded;
17407     },
17408     
17409     hasChildNodes : function(){
17410         if(!this.isLeaf() && !this.loaded){
17411             return true;
17412         }else{
17413             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17414         }
17415     },
17416
17417     /**
17418      * Trigger a reload for this node
17419      * @param {Function} callback
17420      */
17421     reload : function(callback){
17422         this.collapse(false, false);
17423         while(this.firstChild){
17424             this.removeChild(this.firstChild);
17425         }
17426         this.childrenRendered = false;
17427         this.loaded = false;
17428         if(this.isHiddenRoot()){
17429             this.expanded = false;
17430         }
17431         this.expand(false, false, callback);
17432     }
17433 });/*
17434  * Based on:
17435  * Ext JS Library 1.1.1
17436  * Copyright(c) 2006-2007, Ext JS, LLC.
17437  *
17438  * Originally Released Under LGPL - original licence link has changed is not relivant.
17439  *
17440  * Fork - LGPL
17441  * <script type="text/javascript">
17442  */
17443  
17444 /**
17445  * @class Roo.tree.TreeNodeUI
17446  * @constructor
17447  * @param {Object} node The node to render
17448  * The TreeNode UI implementation is separate from the
17449  * tree implementation. Unless you are customizing the tree UI,
17450  * you should never have to use this directly.
17451  */
17452 Roo.tree.TreeNodeUI = function(node){
17453     this.node = node;
17454     this.rendered = false;
17455     this.animating = false;
17456     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17457 };
17458
17459 Roo.tree.TreeNodeUI.prototype = {
17460     removeChild : function(node){
17461         if(this.rendered){
17462             this.ctNode.removeChild(node.ui.getEl());
17463         }
17464     },
17465
17466     beforeLoad : function(){
17467          this.addClass("x-tree-node-loading");
17468     },
17469
17470     afterLoad : function(){
17471          this.removeClass("x-tree-node-loading");
17472     },
17473
17474     onTextChange : function(node, text, oldText){
17475         if(this.rendered){
17476             this.textNode.innerHTML = text;
17477         }
17478     },
17479
17480     onDisableChange : function(node, state){
17481         this.disabled = state;
17482         if(state){
17483             this.addClass("x-tree-node-disabled");
17484         }else{
17485             this.removeClass("x-tree-node-disabled");
17486         }
17487     },
17488
17489     onSelectedChange : function(state){
17490         if(state){
17491             this.focus();
17492             this.addClass("x-tree-selected");
17493         }else{
17494             //this.blur();
17495             this.removeClass("x-tree-selected");
17496         }
17497     },
17498
17499     onMove : function(tree, node, oldParent, newParent, index, refNode){
17500         this.childIndent = null;
17501         if(this.rendered){
17502             var targetNode = newParent.ui.getContainer();
17503             if(!targetNode){//target not rendered
17504                 this.holder = document.createElement("div");
17505                 this.holder.appendChild(this.wrap);
17506                 return;
17507             }
17508             var insertBefore = refNode ? refNode.ui.getEl() : null;
17509             if(insertBefore){
17510                 targetNode.insertBefore(this.wrap, insertBefore);
17511             }else{
17512                 targetNode.appendChild(this.wrap);
17513             }
17514             this.node.renderIndent(true);
17515         }
17516     },
17517
17518     addClass : function(cls){
17519         if(this.elNode){
17520             Roo.fly(this.elNode).addClass(cls);
17521         }
17522     },
17523
17524     removeClass : function(cls){
17525         if(this.elNode){
17526             Roo.fly(this.elNode).removeClass(cls);
17527         }
17528     },
17529
17530     remove : function(){
17531         if(this.rendered){
17532             this.holder = document.createElement("div");
17533             this.holder.appendChild(this.wrap);
17534         }
17535     },
17536
17537     fireEvent : function(){
17538         return this.node.fireEvent.apply(this.node, arguments);
17539     },
17540
17541     initEvents : function(){
17542         this.node.on("move", this.onMove, this);
17543         var E = Roo.EventManager;
17544         var a = this.anchor;
17545
17546         var el = Roo.fly(a, '_treeui');
17547
17548         if(Roo.isOpera){ // opera render bug ignores the CSS
17549             el.setStyle("text-decoration", "none");
17550         }
17551
17552         el.on("click", this.onClick, this);
17553         el.on("dblclick", this.onDblClick, this);
17554
17555         if(this.checkbox){
17556             Roo.EventManager.on(this.checkbox,
17557                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17558         }
17559
17560         el.on("contextmenu", this.onContextMenu, this);
17561
17562         var icon = Roo.fly(this.iconNode);
17563         icon.on("click", this.onClick, this);
17564         icon.on("dblclick", this.onDblClick, this);
17565         icon.on("contextmenu", this.onContextMenu, this);
17566         E.on(this.ecNode, "click", this.ecClick, this, true);
17567
17568         if(this.node.disabled){
17569             this.addClass("x-tree-node-disabled");
17570         }
17571         if(this.node.hidden){
17572             this.addClass("x-tree-node-disabled");
17573         }
17574         var ot = this.node.getOwnerTree();
17575         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17576         if(dd && (!this.node.isRoot || ot.rootVisible)){
17577             Roo.dd.Registry.register(this.elNode, {
17578                 node: this.node,
17579                 handles: this.getDDHandles(),
17580                 isHandle: false
17581             });
17582         }
17583     },
17584
17585     getDDHandles : function(){
17586         return [this.iconNode, this.textNode];
17587     },
17588
17589     hide : function(){
17590         if(this.rendered){
17591             this.wrap.style.display = "none";
17592         }
17593     },
17594
17595     show : function(){
17596         if(this.rendered){
17597             this.wrap.style.display = "";
17598         }
17599     },
17600
17601     onContextMenu : function(e){
17602         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17603             e.preventDefault();
17604             this.focus();
17605             this.fireEvent("contextmenu", this.node, e);
17606         }
17607     },
17608
17609     onClick : function(e){
17610         if(this.dropping){
17611             e.stopEvent();
17612             return;
17613         }
17614         if(this.fireEvent("beforeclick", this.node, e) !== false){
17615             if(!this.disabled && this.node.attributes.href){
17616                 this.fireEvent("click", this.node, e);
17617                 return;
17618             }
17619             e.preventDefault();
17620             if(this.disabled){
17621                 return;
17622             }
17623
17624             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17625                 this.node.toggle();
17626             }
17627
17628             this.fireEvent("click", this.node, e);
17629         }else{
17630             e.stopEvent();
17631         }
17632     },
17633
17634     onDblClick : function(e){
17635         e.preventDefault();
17636         if(this.disabled){
17637             return;
17638         }
17639         if(this.checkbox){
17640             this.toggleCheck();
17641         }
17642         if(!this.animating && this.node.hasChildNodes()){
17643             this.node.toggle();
17644         }
17645         this.fireEvent("dblclick", this.node, e);
17646     },
17647
17648     onCheckChange : function(){
17649         var checked = this.checkbox.checked;
17650         this.node.attributes.checked = checked;
17651         this.fireEvent('checkchange', this.node, checked);
17652     },
17653
17654     ecClick : function(e){
17655         if(!this.animating && this.node.hasChildNodes()){
17656             this.node.toggle();
17657         }
17658     },
17659
17660     startDrop : function(){
17661         this.dropping = true;
17662     },
17663
17664     // delayed drop so the click event doesn't get fired on a drop
17665     endDrop : function(){
17666        setTimeout(function(){
17667            this.dropping = false;
17668        }.createDelegate(this), 50);
17669     },
17670
17671     expand : function(){
17672         this.updateExpandIcon();
17673         this.ctNode.style.display = "";
17674     },
17675
17676     focus : function(){
17677         if(!this.node.preventHScroll){
17678             try{this.anchor.focus();
17679             }catch(e){}
17680         }else if(!Roo.isIE){
17681             try{
17682                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17683                 var l = noscroll.scrollLeft;
17684                 this.anchor.focus();
17685                 noscroll.scrollLeft = l;
17686             }catch(e){}
17687         }
17688     },
17689
17690     toggleCheck : function(value){
17691         var cb = this.checkbox;
17692         if(cb){
17693             cb.checked = (value === undefined ? !cb.checked : value);
17694         }
17695     },
17696
17697     blur : function(){
17698         try{
17699             this.anchor.blur();
17700         }catch(e){}
17701     },
17702
17703     animExpand : function(callback){
17704         var ct = Roo.get(this.ctNode);
17705         ct.stopFx();
17706         if(!this.node.hasChildNodes()){
17707             this.updateExpandIcon();
17708             this.ctNode.style.display = "";
17709             Roo.callback(callback);
17710             return;
17711         }
17712         this.animating = true;
17713         this.updateExpandIcon();
17714
17715         ct.slideIn('t', {
17716            callback : function(){
17717                this.animating = false;
17718                Roo.callback(callback);
17719             },
17720             scope: this,
17721             duration: this.node.ownerTree.duration || .25
17722         });
17723     },
17724
17725     highlight : function(){
17726         var tree = this.node.getOwnerTree();
17727         Roo.fly(this.wrap).highlight(
17728             tree.hlColor || "C3DAF9",
17729             {endColor: tree.hlBaseColor}
17730         );
17731     },
17732
17733     collapse : function(){
17734         this.updateExpandIcon();
17735         this.ctNode.style.display = "none";
17736     },
17737
17738     animCollapse : function(callback){
17739         var ct = Roo.get(this.ctNode);
17740         ct.enableDisplayMode('block');
17741         ct.stopFx();
17742
17743         this.animating = true;
17744         this.updateExpandIcon();
17745
17746         ct.slideOut('t', {
17747             callback : function(){
17748                this.animating = false;
17749                Roo.callback(callback);
17750             },
17751             scope: this,
17752             duration: this.node.ownerTree.duration || .25
17753         });
17754     },
17755
17756     getContainer : function(){
17757         return this.ctNode;
17758     },
17759
17760     getEl : function(){
17761         return this.wrap;
17762     },
17763
17764     appendDDGhost : function(ghostNode){
17765         ghostNode.appendChild(this.elNode.cloneNode(true));
17766     },
17767
17768     getDDRepairXY : function(){
17769         return Roo.lib.Dom.getXY(this.iconNode);
17770     },
17771
17772     onRender : function(){
17773         this.render();
17774     },
17775
17776     render : function(bulkRender){
17777         var n = this.node, a = n.attributes;
17778         var targetNode = n.parentNode ?
17779               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17780
17781         if(!this.rendered){
17782             this.rendered = true;
17783
17784             this.renderElements(n, a, targetNode, bulkRender);
17785
17786             if(a.qtip){
17787                if(this.textNode.setAttributeNS){
17788                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17789                    if(a.qtipTitle){
17790                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17791                    }
17792                }else{
17793                    this.textNode.setAttribute("ext:qtip", a.qtip);
17794                    if(a.qtipTitle){
17795                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17796                    }
17797                }
17798             }else if(a.qtipCfg){
17799                 a.qtipCfg.target = Roo.id(this.textNode);
17800                 Roo.QuickTips.register(a.qtipCfg);
17801             }
17802             this.initEvents();
17803             if(!this.node.expanded){
17804                 this.updateExpandIcon();
17805             }
17806         }else{
17807             if(bulkRender === true) {
17808                 targetNode.appendChild(this.wrap);
17809             }
17810         }
17811     },
17812
17813     renderElements : function(n, a, targetNode, bulkRender){
17814         // add some indent caching, this helps performance when rendering a large tree
17815         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17816         var t = n.getOwnerTree();
17817         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17818         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17819         var cb = typeof a.checked == 'boolean';
17820         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17821         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17822             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17823             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17824             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17825             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17826             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17827              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17828                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17829             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17830             "</li>"];
17831
17832         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17833             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17834                                 n.nextSibling.ui.getEl(), buf.join(""));
17835         }else{
17836             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17837         }
17838
17839         this.elNode = this.wrap.childNodes[0];
17840         this.ctNode = this.wrap.childNodes[1];
17841         var cs = this.elNode.childNodes;
17842         this.indentNode = cs[0];
17843         this.ecNode = cs[1];
17844         this.iconNode = cs[2];
17845         var index = 3;
17846         if(cb){
17847             this.checkbox = cs[3];
17848             index++;
17849         }
17850         this.anchor = cs[index];
17851         this.textNode = cs[index].firstChild;
17852     },
17853
17854     getAnchor : function(){
17855         return this.anchor;
17856     },
17857
17858     getTextEl : function(){
17859         return this.textNode;
17860     },
17861
17862     getIconEl : function(){
17863         return this.iconNode;
17864     },
17865
17866     isChecked : function(){
17867         return this.checkbox ? this.checkbox.checked : false;
17868     },
17869
17870     updateExpandIcon : function(){
17871         if(this.rendered){
17872             var n = this.node, c1, c2;
17873             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17874             var hasChild = n.hasChildNodes();
17875             if(hasChild){
17876                 if(n.expanded){
17877                     cls += "-minus";
17878                     c1 = "x-tree-node-collapsed";
17879                     c2 = "x-tree-node-expanded";
17880                 }else{
17881                     cls += "-plus";
17882                     c1 = "x-tree-node-expanded";
17883                     c2 = "x-tree-node-collapsed";
17884                 }
17885                 if(this.wasLeaf){
17886                     this.removeClass("x-tree-node-leaf");
17887                     this.wasLeaf = false;
17888                 }
17889                 if(this.c1 != c1 || this.c2 != c2){
17890                     Roo.fly(this.elNode).replaceClass(c1, c2);
17891                     this.c1 = c1; this.c2 = c2;
17892                 }
17893             }else{
17894                 if(!this.wasLeaf){
17895                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17896                     delete this.c1;
17897                     delete this.c2;
17898                     this.wasLeaf = true;
17899                 }
17900             }
17901             var ecc = "x-tree-ec-icon "+cls;
17902             if(this.ecc != ecc){
17903                 this.ecNode.className = ecc;
17904                 this.ecc = ecc;
17905             }
17906         }
17907     },
17908
17909     getChildIndent : function(){
17910         if(!this.childIndent){
17911             var buf = [];
17912             var p = this.node;
17913             while(p){
17914                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17915                     if(!p.isLast()) {
17916                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17917                     } else {
17918                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17919                     }
17920                 }
17921                 p = p.parentNode;
17922             }
17923             this.childIndent = buf.join("");
17924         }
17925         return this.childIndent;
17926     },
17927
17928     renderIndent : function(){
17929         if(this.rendered){
17930             var indent = "";
17931             var p = this.node.parentNode;
17932             if(p){
17933                 indent = p.ui.getChildIndent();
17934             }
17935             if(this.indentMarkup != indent){ // don't rerender if not required
17936                 this.indentNode.innerHTML = indent;
17937                 this.indentMarkup = indent;
17938             }
17939             this.updateExpandIcon();
17940         }
17941     }
17942 };
17943
17944 Roo.tree.RootTreeNodeUI = function(){
17945     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17946 };
17947 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17948     render : function(){
17949         if(!this.rendered){
17950             var targetNode = this.node.ownerTree.innerCt.dom;
17951             this.node.expanded = true;
17952             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17953             this.wrap = this.ctNode = targetNode.firstChild;
17954         }
17955     },
17956     collapse : function(){
17957     },
17958     expand : function(){
17959     }
17960 });/*
17961  * Based on:
17962  * Ext JS Library 1.1.1
17963  * Copyright(c) 2006-2007, Ext JS, LLC.
17964  *
17965  * Originally Released Under LGPL - original licence link has changed is not relivant.
17966  *
17967  * Fork - LGPL
17968  * <script type="text/javascript">
17969  */
17970 /**
17971  * @class Roo.tree.TreeLoader
17972  * @extends Roo.util.Observable
17973  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17974  * nodes from a specified URL. The response must be a javascript Array definition
17975  * who's elements are node definition objects. eg:
17976  * <pre><code>
17977    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
17978     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
17979 </code></pre>
17980  * <br><br>
17981  * A server request is sent, and child nodes are loaded only when a node is expanded.
17982  * The loading node's id is passed to the server under the parameter name "node" to
17983  * enable the server to produce the correct child nodes.
17984  * <br><br>
17985  * To pass extra parameters, an event handler may be attached to the "beforeload"
17986  * event, and the parameters specified in the TreeLoader's baseParams property:
17987  * <pre><code>
17988     myTreeLoader.on("beforeload", function(treeLoader, node) {
17989         this.baseParams.category = node.attributes.category;
17990     }, this);
17991 </code></pre><
17992  * This would pass an HTTP parameter called "category" to the server containing
17993  * the value of the Node's "category" attribute.
17994  * @constructor
17995  * Creates a new Treeloader.
17996  * @param {Object} config A config object containing config properties.
17997  */
17998 Roo.tree.TreeLoader = function(config){
17999     this.baseParams = {};
18000     this.requestMethod = "POST";
18001     Roo.apply(this, config);
18002
18003     this.addEvents({
18004     
18005         /**
18006          * @event beforeload
18007          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18008          * @param {Object} This TreeLoader object.
18009          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18010          * @param {Object} callback The callback function specified in the {@link #load} call.
18011          */
18012         beforeload : true,
18013         /**
18014          * @event load
18015          * Fires when the node has been successfuly loaded.
18016          * @param {Object} This TreeLoader object.
18017          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18018          * @param {Object} response The response object containing the data from the server.
18019          */
18020         load : true,
18021         /**
18022          * @event loadexception
18023          * Fires if the network request failed.
18024          * @param {Object} This TreeLoader object.
18025          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18026          * @param {Object} response The response object containing the data from the server.
18027          */
18028         loadexception : true,
18029         /**
18030          * @event create
18031          * Fires before a node is created, enabling you to return custom Node types 
18032          * @param {Object} This TreeLoader object.
18033          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18034          */
18035         create : true
18036     });
18037
18038     Roo.tree.TreeLoader.superclass.constructor.call(this);
18039 };
18040
18041 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18042     /**
18043     * @cfg {String} dataUrl The URL from which to request a Json string which
18044     * specifies an array of node definition object representing the child nodes
18045     * to be loaded.
18046     */
18047     /**
18048     * @cfg {Object} baseParams (optional) An object containing properties which
18049     * specify HTTP parameters to be passed to each request for child nodes.
18050     */
18051     /**
18052     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18053     * created by this loader. If the attributes sent by the server have an attribute in this object,
18054     * they take priority.
18055     */
18056     /**
18057     * @cfg {Object} uiProviders (optional) An object containing properties which
18058     * 
18059     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18060     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18061     * <i>uiProvider</i> attribute of a returned child node is a string rather
18062     * than a reference to a TreeNodeUI implementation, this that string value
18063     * is used as a property name in the uiProviders object. You can define the provider named
18064     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18065     */
18066     uiProviders : {},
18067
18068     /**
18069     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18070     * child nodes before loading.
18071     */
18072     clearOnLoad : true,
18073
18074     /**
18075     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18076     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18077     * Grid query { data : [ .....] }
18078     */
18079     
18080     root : false,
18081      /**
18082     * @cfg {String} queryParam (optional) 
18083     * Name of the query as it will be passed on the querystring (defaults to 'node')
18084     * eg. the request will be ?node=[id]
18085     */
18086     
18087     
18088     queryParam: false,
18089     
18090     /**
18091      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18092      * This is called automatically when a node is expanded, but may be used to reload
18093      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18094      * @param {Roo.tree.TreeNode} node
18095      * @param {Function} callback
18096      */
18097     load : function(node, callback){
18098         if(this.clearOnLoad){
18099             while(node.firstChild){
18100                 node.removeChild(node.firstChild);
18101             }
18102         }
18103         if(node.attributes.children){ // preloaded json children
18104             var cs = node.attributes.children;
18105             for(var i = 0, len = cs.length; i < len; i++){
18106                 node.appendChild(this.createNode(cs[i]));
18107             }
18108             if(typeof callback == "function"){
18109                 callback();
18110             }
18111         }else if(this.dataUrl){
18112             this.requestData(node, callback);
18113         }
18114     },
18115
18116     getParams: function(node){
18117         var buf = [], bp = this.baseParams;
18118         for(var key in bp){
18119             if(typeof bp[key] != "function"){
18120                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18121             }
18122         }
18123         var n = this.queryParam === false ? 'node' : this.queryParam;
18124         buf.push(n + "=", encodeURIComponent(node.id));
18125         return buf.join("");
18126     },
18127
18128     requestData : function(node, callback){
18129         if(this.fireEvent("beforeload", this, node, callback) !== false){
18130             this.transId = Roo.Ajax.request({
18131                 method:this.requestMethod,
18132                 url: this.dataUrl||this.url,
18133                 success: this.handleResponse,
18134                 failure: this.handleFailure,
18135                 scope: this,
18136                 argument: {callback: callback, node: node},
18137                 params: this.getParams(node)
18138             });
18139         }else{
18140             // if the load is cancelled, make sure we notify
18141             // the node that we are done
18142             if(typeof callback == "function"){
18143                 callback();
18144             }
18145         }
18146     },
18147
18148     isLoading : function(){
18149         return this.transId ? true : false;
18150     },
18151
18152     abort : function(){
18153         if(this.isLoading()){
18154             Roo.Ajax.abort(this.transId);
18155         }
18156     },
18157
18158     // private
18159     createNode : function(attr){
18160         // apply baseAttrs, nice idea Corey!
18161         if(this.baseAttrs){
18162             Roo.applyIf(attr, this.baseAttrs);
18163         }
18164         if(this.applyLoader !== false){
18165             attr.loader = this;
18166         }
18167         // uiProvider = depreciated..
18168         
18169         if(typeof(attr.uiProvider) == 'string'){
18170            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18171                 /**  eval:var:attr */ eval(attr.uiProvider);
18172         }
18173         if(typeof(this.uiProviders['default']) != 'undefined') {
18174             attr.uiProvider = this.uiProviders['default'];
18175         }
18176         
18177         this.fireEvent('create', this, attr);
18178         
18179         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18180         return(attr.leaf ?
18181                         new Roo.tree.TreeNode(attr) :
18182                         new Roo.tree.AsyncTreeNode(attr));
18183     },
18184
18185     processResponse : function(response, node, callback){
18186         var json = response.responseText;
18187         try {
18188             
18189             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18190             if (this.root !== false) {
18191                 o = o[this.root];
18192             }
18193             
18194             for(var i = 0, len = o.length; i < len; i++){
18195                 var n = this.createNode(o[i]);
18196                 if(n){
18197                     node.appendChild(n);
18198                 }
18199             }
18200             if(typeof callback == "function"){
18201                 callback(this, node);
18202             }
18203         }catch(e){
18204             this.handleFailure(response);
18205         }
18206     },
18207
18208     handleResponse : function(response){
18209         this.transId = false;
18210         var a = response.argument;
18211         this.processResponse(response, a.node, a.callback);
18212         this.fireEvent("load", this, a.node, response);
18213     },
18214
18215     handleFailure : function(response){
18216         this.transId = false;
18217         var a = response.argument;
18218         this.fireEvent("loadexception", this, a.node, response);
18219         if(typeof a.callback == "function"){
18220             a.callback(this, a.node);
18221         }
18222     }
18223 });/*
18224  * Based on:
18225  * Ext JS Library 1.1.1
18226  * Copyright(c) 2006-2007, Ext JS, LLC.
18227  *
18228  * Originally Released Under LGPL - original licence link has changed is not relivant.
18229  *
18230  * Fork - LGPL
18231  * <script type="text/javascript">
18232  */
18233
18234 /**
18235 * @class Roo.tree.TreeFilter
18236 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18237 * @param {TreePanel} tree
18238 * @param {Object} config (optional)
18239  */
18240 Roo.tree.TreeFilter = function(tree, config){
18241     this.tree = tree;
18242     this.filtered = {};
18243     Roo.apply(this, config);
18244 };
18245
18246 Roo.tree.TreeFilter.prototype = {
18247     clearBlank:false,
18248     reverse:false,
18249     autoClear:false,
18250     remove:false,
18251
18252      /**
18253      * Filter the data by a specific attribute.
18254      * @param {String/RegExp} value Either string that the attribute value
18255      * should start with or a RegExp to test against the attribute
18256      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18257      * @param {TreeNode} startNode (optional) The node to start the filter at.
18258      */
18259     filter : function(value, attr, startNode){
18260         attr = attr || "text";
18261         var f;
18262         if(typeof value == "string"){
18263             var vlen = value.length;
18264             // auto clear empty filter
18265             if(vlen == 0 && this.clearBlank){
18266                 this.clear();
18267                 return;
18268             }
18269             value = value.toLowerCase();
18270             f = function(n){
18271                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18272             };
18273         }else if(value.exec){ // regex?
18274             f = function(n){
18275                 return value.test(n.attributes[attr]);
18276             };
18277         }else{
18278             throw 'Illegal filter type, must be string or regex';
18279         }
18280         this.filterBy(f, null, startNode);
18281         },
18282
18283     /**
18284      * Filter by a function. The passed function will be called with each
18285      * node in the tree (or from the startNode). If the function returns true, the node is kept
18286      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18287      * @param {Function} fn The filter function
18288      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18289      */
18290     filterBy : function(fn, scope, startNode){
18291         startNode = startNode || this.tree.root;
18292         if(this.autoClear){
18293             this.clear();
18294         }
18295         var af = this.filtered, rv = this.reverse;
18296         var f = function(n){
18297             if(n == startNode){
18298                 return true;
18299             }
18300             if(af[n.id]){
18301                 return false;
18302             }
18303             var m = fn.call(scope || n, n);
18304             if(!m || rv){
18305                 af[n.id] = n;
18306                 n.ui.hide();
18307                 return false;
18308             }
18309             return true;
18310         };
18311         startNode.cascade(f);
18312         if(this.remove){
18313            for(var id in af){
18314                if(typeof id != "function"){
18315                    var n = af[id];
18316                    if(n && n.parentNode){
18317                        n.parentNode.removeChild(n);
18318                    }
18319                }
18320            }
18321         }
18322     },
18323
18324     /**
18325      * Clears the current filter. Note: with the "remove" option
18326      * set a filter cannot be cleared.
18327      */
18328     clear : function(){
18329         var t = this.tree;
18330         var af = this.filtered;
18331         for(var id in af){
18332             if(typeof id != "function"){
18333                 var n = af[id];
18334                 if(n){
18335                     n.ui.show();
18336                 }
18337             }
18338         }
18339         this.filtered = {};
18340     }
18341 };
18342 /*
18343  * Based on:
18344  * Ext JS Library 1.1.1
18345  * Copyright(c) 2006-2007, Ext JS, LLC.
18346  *
18347  * Originally Released Under LGPL - original licence link has changed is not relivant.
18348  *
18349  * Fork - LGPL
18350  * <script type="text/javascript">
18351  */
18352  
18353
18354 /**
18355  * @class Roo.tree.TreeSorter
18356  * Provides sorting of nodes in a TreePanel
18357  * 
18358  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18359  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18360  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18361  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18362  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18363  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18364  * @constructor
18365  * @param {TreePanel} tree
18366  * @param {Object} config
18367  */
18368 Roo.tree.TreeSorter = function(tree, config){
18369     Roo.apply(this, config);
18370     tree.on("beforechildrenrendered", this.doSort, this);
18371     tree.on("append", this.updateSort, this);
18372     tree.on("insert", this.updateSort, this);
18373     
18374     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18375     var p = this.property || "text";
18376     var sortType = this.sortType;
18377     var fs = this.folderSort;
18378     var cs = this.caseSensitive === true;
18379     var leafAttr = this.leafAttr || 'leaf';
18380
18381     this.sortFn = function(n1, n2){
18382         if(fs){
18383             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18384                 return 1;
18385             }
18386             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18387                 return -1;
18388             }
18389         }
18390         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18391         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18392         if(v1 < v2){
18393                         return dsc ? +1 : -1;
18394                 }else if(v1 > v2){
18395                         return dsc ? -1 : +1;
18396         }else{
18397                 return 0;
18398         }
18399     };
18400 };
18401
18402 Roo.tree.TreeSorter.prototype = {
18403     doSort : function(node){
18404         node.sort(this.sortFn);
18405     },
18406     
18407     compareNodes : function(n1, n2){
18408         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18409     },
18410     
18411     updateSort : function(tree, node){
18412         if(node.childrenRendered){
18413             this.doSort.defer(1, this, [node]);
18414         }
18415     }
18416 };/*
18417  * Based on:
18418  * Ext JS Library 1.1.1
18419  * Copyright(c) 2006-2007, Ext JS, LLC.
18420  *
18421  * Originally Released Under LGPL - original licence link has changed is not relivant.
18422  *
18423  * Fork - LGPL
18424  * <script type="text/javascript">
18425  */
18426
18427 if(Roo.dd.DropZone){
18428     
18429 Roo.tree.TreeDropZone = function(tree, config){
18430     this.allowParentInsert = false;
18431     this.allowContainerDrop = false;
18432     this.appendOnly = false;
18433     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18434     this.tree = tree;
18435     this.lastInsertClass = "x-tree-no-status";
18436     this.dragOverData = {};
18437 };
18438
18439 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18440     ddGroup : "TreeDD",
18441     
18442     expandDelay : 1000,
18443     
18444     expandNode : function(node){
18445         if(node.hasChildNodes() && !node.isExpanded()){
18446             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18447         }
18448     },
18449     
18450     queueExpand : function(node){
18451         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18452     },
18453     
18454     cancelExpand : function(){
18455         if(this.expandProcId){
18456             clearTimeout(this.expandProcId);
18457             this.expandProcId = false;
18458         }
18459     },
18460     
18461     isValidDropPoint : function(n, pt, dd, e, data){
18462         if(!n || !data){ return false; }
18463         var targetNode = n.node;
18464         var dropNode = data.node;
18465         // default drop rules
18466         if(!(targetNode && targetNode.isTarget && pt)){
18467             return false;
18468         }
18469         if(pt == "append" && targetNode.allowChildren === false){
18470             return false;
18471         }
18472         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18473             return false;
18474         }
18475         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18476             return false;
18477         }
18478         // reuse the object
18479         var overEvent = this.dragOverData;
18480         overEvent.tree = this.tree;
18481         overEvent.target = targetNode;
18482         overEvent.data = data;
18483         overEvent.point = pt;
18484         overEvent.source = dd;
18485         overEvent.rawEvent = e;
18486         overEvent.dropNode = dropNode;
18487         overEvent.cancel = false;  
18488         var result = this.tree.fireEvent("nodedragover", overEvent);
18489         return overEvent.cancel === false && result !== false;
18490     },
18491     
18492     getDropPoint : function(e, n, dd){
18493         var tn = n.node;
18494         if(tn.isRoot){
18495             return tn.allowChildren !== false ? "append" : false; // always append for root
18496         }
18497         var dragEl = n.ddel;
18498         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18499         var y = Roo.lib.Event.getPageY(e);
18500         var noAppend = tn.allowChildren === false || tn.isLeaf();
18501         if(this.appendOnly || tn.parentNode.allowChildren === false){
18502             return noAppend ? false : "append";
18503         }
18504         var noBelow = false;
18505         if(!this.allowParentInsert){
18506             noBelow = tn.hasChildNodes() && tn.isExpanded();
18507         }
18508         var q = (b - t) / (noAppend ? 2 : 3);
18509         if(y >= t && y < (t + q)){
18510             return "above";
18511         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18512             return "below";
18513         }else{
18514             return "append";
18515         }
18516     },
18517     
18518     onNodeEnter : function(n, dd, e, data){
18519         this.cancelExpand();
18520     },
18521     
18522     onNodeOver : function(n, dd, e, data){
18523         var pt = this.getDropPoint(e, n, dd);
18524         var node = n.node;
18525         
18526         // auto node expand check
18527         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18528             this.queueExpand(node);
18529         }else if(pt != "append"){
18530             this.cancelExpand();
18531         }
18532         
18533         // set the insert point style on the target node
18534         var returnCls = this.dropNotAllowed;
18535         if(this.isValidDropPoint(n, pt, dd, e, data)){
18536            if(pt){
18537                var el = n.ddel;
18538                var cls;
18539                if(pt == "above"){
18540                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18541                    cls = "x-tree-drag-insert-above";
18542                }else if(pt == "below"){
18543                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18544                    cls = "x-tree-drag-insert-below";
18545                }else{
18546                    returnCls = "x-tree-drop-ok-append";
18547                    cls = "x-tree-drag-append";
18548                }
18549                if(this.lastInsertClass != cls){
18550                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18551                    this.lastInsertClass = cls;
18552                }
18553            }
18554        }
18555        return returnCls;
18556     },
18557     
18558     onNodeOut : function(n, dd, e, data){
18559         this.cancelExpand();
18560         this.removeDropIndicators(n);
18561     },
18562     
18563     onNodeDrop : function(n, dd, e, data){
18564         var point = this.getDropPoint(e, n, dd);
18565         var targetNode = n.node;
18566         targetNode.ui.startDrop();
18567         if(!this.isValidDropPoint(n, point, dd, e, data)){
18568             targetNode.ui.endDrop();
18569             return false;
18570         }
18571         // first try to find the drop node
18572         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18573         var dropEvent = {
18574             tree : this.tree,
18575             target: targetNode,
18576             data: data,
18577             point: point,
18578             source: dd,
18579             rawEvent: e,
18580             dropNode: dropNode,
18581             cancel: !dropNode   
18582         };
18583         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18584         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18585             targetNode.ui.endDrop();
18586             return false;
18587         }
18588         // allow target changing
18589         targetNode = dropEvent.target;
18590         if(point == "append" && !targetNode.isExpanded()){
18591             targetNode.expand(false, null, function(){
18592                 this.completeDrop(dropEvent);
18593             }.createDelegate(this));
18594         }else{
18595             this.completeDrop(dropEvent);
18596         }
18597         return true;
18598     },
18599     
18600     completeDrop : function(de){
18601         var ns = de.dropNode, p = de.point, t = de.target;
18602         if(!(ns instanceof Array)){
18603             ns = [ns];
18604         }
18605         var n;
18606         for(var i = 0, len = ns.length; i < len; i++){
18607             n = ns[i];
18608             if(p == "above"){
18609                 t.parentNode.insertBefore(n, t);
18610             }else if(p == "below"){
18611                 t.parentNode.insertBefore(n, t.nextSibling);
18612             }else{
18613                 t.appendChild(n);
18614             }
18615         }
18616         n.ui.focus();
18617         if(this.tree.hlDrop){
18618             n.ui.highlight();
18619         }
18620         t.ui.endDrop();
18621         this.tree.fireEvent("nodedrop", de);
18622     },
18623     
18624     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18625         if(this.tree.hlDrop){
18626             dropNode.ui.focus();
18627             dropNode.ui.highlight();
18628         }
18629         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18630     },
18631     
18632     getTree : function(){
18633         return this.tree;
18634     },
18635     
18636     removeDropIndicators : function(n){
18637         if(n && n.ddel){
18638             var el = n.ddel;
18639             Roo.fly(el).removeClass([
18640                     "x-tree-drag-insert-above",
18641                     "x-tree-drag-insert-below",
18642                     "x-tree-drag-append"]);
18643             this.lastInsertClass = "_noclass";
18644         }
18645     },
18646     
18647     beforeDragDrop : function(target, e, id){
18648         this.cancelExpand();
18649         return true;
18650     },
18651     
18652     afterRepair : function(data){
18653         if(data && Roo.enableFx){
18654             data.node.ui.highlight();
18655         }
18656         this.hideProxy();
18657     }    
18658 });
18659
18660 }/*
18661  * Based on:
18662  * Ext JS Library 1.1.1
18663  * Copyright(c) 2006-2007, Ext JS, LLC.
18664  *
18665  * Originally Released Under LGPL - original licence link has changed is not relivant.
18666  *
18667  * Fork - LGPL
18668  * <script type="text/javascript">
18669  */
18670  
18671
18672 if(Roo.dd.DragZone){
18673 Roo.tree.TreeDragZone = function(tree, config){
18674     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18675     this.tree = tree;
18676 };
18677
18678 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18679     ddGroup : "TreeDD",
18680     
18681     onBeforeDrag : function(data, e){
18682         var n = data.node;
18683         return n && n.draggable && !n.disabled;
18684     },
18685     
18686     onInitDrag : function(e){
18687         var data = this.dragData;
18688         this.tree.getSelectionModel().select(data.node);
18689         this.proxy.update("");
18690         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18691         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18692     },
18693     
18694     getRepairXY : function(e, data){
18695         return data.node.ui.getDDRepairXY();
18696     },
18697     
18698     onEndDrag : function(data, e){
18699         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18700     },
18701     
18702     onValidDrop : function(dd, e, id){
18703         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18704         this.hideProxy();
18705     },
18706     
18707     beforeInvalidDrop : function(e, id){
18708         // this scrolls the original position back into view
18709         var sm = this.tree.getSelectionModel();
18710         sm.clearSelections();
18711         sm.select(this.dragData.node);
18712     }
18713 });
18714 }/*
18715  * Based on:
18716  * Ext JS Library 1.1.1
18717  * Copyright(c) 2006-2007, Ext JS, LLC.
18718  *
18719  * Originally Released Under LGPL - original licence link has changed is not relivant.
18720  *
18721  * Fork - LGPL
18722  * <script type="text/javascript">
18723  */
18724 /**
18725  * @class Roo.tree.TreeEditor
18726  * @extends Roo.Editor
18727  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18728  * as the editor field.
18729  * @constructor
18730  * @param {TreePanel} tree
18731  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18732  */
18733 Roo.tree.TreeEditor = function(tree, config){
18734     config = config || {};
18735     var field = config.events ? config : new Roo.form.TextField(config);
18736     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18737
18738     this.tree = tree;
18739
18740     tree.on('beforeclick', this.beforeNodeClick, this);
18741     tree.getTreeEl().on('mousedown', this.hide, this);
18742     this.on('complete', this.updateNode, this);
18743     this.on('beforestartedit', this.fitToTree, this);
18744     this.on('startedit', this.bindScroll, this, {delay:10});
18745     this.on('specialkey', this.onSpecialKey, this);
18746 };
18747
18748 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18749     /**
18750      * @cfg {String} alignment
18751      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18752      */
18753     alignment: "l-l",
18754     // inherit
18755     autoSize: false,
18756     /**
18757      * @cfg {Boolean} hideEl
18758      * True to hide the bound element while the editor is displayed (defaults to false)
18759      */
18760     hideEl : false,
18761     /**
18762      * @cfg {String} cls
18763      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18764      */
18765     cls: "x-small-editor x-tree-editor",
18766     /**
18767      * @cfg {Boolean} shim
18768      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18769      */
18770     shim:false,
18771     // inherit
18772     shadow:"frame",
18773     /**
18774      * @cfg {Number} maxWidth
18775      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18776      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18777      * scroll and client offsets into account prior to each edit.
18778      */
18779     maxWidth: 250,
18780
18781     editDelay : 350,
18782
18783     // private
18784     fitToTree : function(ed, el){
18785         var td = this.tree.getTreeEl().dom, nd = el.dom;
18786         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18787             td.scrollLeft = nd.offsetLeft;
18788         }
18789         var w = Math.min(
18790                 this.maxWidth,
18791                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18792         this.setSize(w, '');
18793     },
18794
18795     // private
18796     triggerEdit : function(node){
18797         this.completeEdit();
18798         this.editNode = node;
18799         this.startEdit(node.ui.textNode, node.text);
18800     },
18801
18802     // private
18803     bindScroll : function(){
18804         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18805     },
18806
18807     // private
18808     beforeNodeClick : function(node, e){
18809         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18810         this.lastClick = new Date();
18811         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18812             e.stopEvent();
18813             this.triggerEdit(node);
18814             return false;
18815         }
18816     },
18817
18818     // private
18819     updateNode : function(ed, value){
18820         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18821         this.editNode.setText(value);
18822     },
18823
18824     // private
18825     onHide : function(){
18826         Roo.tree.TreeEditor.superclass.onHide.call(this);
18827         if(this.editNode){
18828             this.editNode.ui.focus();
18829         }
18830     },
18831
18832     // private
18833     onSpecialKey : function(field, e){
18834         var k = e.getKey();
18835         if(k == e.ESC){
18836             e.stopEvent();
18837             this.cancelEdit();
18838         }else if(k == e.ENTER && !e.hasModifier()){
18839             e.stopEvent();
18840             this.completeEdit();
18841         }
18842     }
18843 });//<Script type="text/javascript">
18844 /*
18845  * Based on:
18846  * Ext JS Library 1.1.1
18847  * Copyright(c) 2006-2007, Ext JS, LLC.
18848  *
18849  * Originally Released Under LGPL - original licence link has changed is not relivant.
18850  *
18851  * Fork - LGPL
18852  * <script type="text/javascript">
18853  */
18854  
18855 /**
18856  * Not documented??? - probably should be...
18857  */
18858
18859 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18860     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18861     
18862     renderElements : function(n, a, targetNode, bulkRender){
18863         //consel.log("renderElements?");
18864         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18865
18866         var t = n.getOwnerTree();
18867         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18868         
18869         var cols = t.columns;
18870         var bw = t.borderWidth;
18871         var c = cols[0];
18872         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18873          var cb = typeof a.checked == "boolean";
18874         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18875         var colcls = 'x-t-' + tid + '-c0';
18876         var buf = [
18877             '<li class="x-tree-node">',
18878             
18879                 
18880                 '<div class="x-tree-node-el ', a.cls,'">',
18881                     // extran...
18882                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18883                 
18884                 
18885                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18886                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18887                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18888                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18889                            (a.iconCls ? ' '+a.iconCls : ''),
18890                            '" unselectable="on" />',
18891                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18892                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18893                              
18894                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18895                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18896                             '<span unselectable="on" qtip="' + tx + '">',
18897                              tx,
18898                              '</span></a>' ,
18899                     '</div>',
18900                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18901                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18902                  ];
18903         
18904         for(var i = 1, len = cols.length; i < len; i++){
18905             c = cols[i];
18906             colcls = 'x-t-' + tid + '-c' +i;
18907             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18908             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18909                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18910                       "</div>");
18911          }
18912          
18913          buf.push(
18914             '</a>',
18915             '<div class="x-clear"></div></div>',
18916             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18917             "</li>");
18918         
18919         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18920             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18921                                 n.nextSibling.ui.getEl(), buf.join(""));
18922         }else{
18923             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18924         }
18925         var el = this.wrap.firstChild;
18926         this.elRow = el;
18927         this.elNode = el.firstChild;
18928         this.ranchor = el.childNodes[1];
18929         this.ctNode = this.wrap.childNodes[1];
18930         var cs = el.firstChild.childNodes;
18931         this.indentNode = cs[0];
18932         this.ecNode = cs[1];
18933         this.iconNode = cs[2];
18934         var index = 3;
18935         if(cb){
18936             this.checkbox = cs[3];
18937             index++;
18938         }
18939         this.anchor = cs[index];
18940         
18941         this.textNode = cs[index].firstChild;
18942         
18943         //el.on("click", this.onClick, this);
18944         //el.on("dblclick", this.onDblClick, this);
18945         
18946         
18947        // console.log(this);
18948     },
18949     initEvents : function(){
18950         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18951         
18952             
18953         var a = this.ranchor;
18954
18955         var el = Roo.get(a);
18956
18957         if(Roo.isOpera){ // opera render bug ignores the CSS
18958             el.setStyle("text-decoration", "none");
18959         }
18960
18961         el.on("click", this.onClick, this);
18962         el.on("dblclick", this.onDblClick, this);
18963         el.on("contextmenu", this.onContextMenu, this);
18964         
18965     },
18966     
18967     /*onSelectedChange : function(state){
18968         if(state){
18969             this.focus();
18970             this.addClass("x-tree-selected");
18971         }else{
18972             //this.blur();
18973             this.removeClass("x-tree-selected");
18974         }
18975     },*/
18976     addClass : function(cls){
18977         if(this.elRow){
18978             Roo.fly(this.elRow).addClass(cls);
18979         }
18980         
18981     },
18982     
18983     
18984     removeClass : function(cls){
18985         if(this.elRow){
18986             Roo.fly(this.elRow).removeClass(cls);
18987         }
18988     }
18989
18990     
18991     
18992 });//<Script type="text/javascript">
18993
18994 /*
18995  * Based on:
18996  * Ext JS Library 1.1.1
18997  * Copyright(c) 2006-2007, Ext JS, LLC.
18998  *
18999  * Originally Released Under LGPL - original licence link has changed is not relivant.
19000  *
19001  * Fork - LGPL
19002  * <script type="text/javascript">
19003  */
19004  
19005
19006 /**
19007  * @class Roo.tree.ColumnTree
19008  * @extends Roo.data.TreePanel
19009  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19010  * @cfg {int} borderWidth  compined right/left border allowance
19011  * @constructor
19012  * @param {String/HTMLElement/Element} el The container element
19013  * @param {Object} config
19014  */
19015 Roo.tree.ColumnTree =  function(el, config)
19016 {
19017    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19018    this.addEvents({
19019         /**
19020         * @event resize
19021         * Fire this event on a container when it resizes
19022         * @param {int} w Width
19023         * @param {int} h Height
19024         */
19025        "resize" : true
19026     });
19027     this.on('resize', this.onResize, this);
19028 };
19029
19030 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19031     //lines:false,
19032     
19033     
19034     borderWidth: Roo.isBorderBox ? 0 : 2, 
19035     headEls : false,
19036     
19037     render : function(){
19038         // add the header.....
19039        
19040         Roo.tree.ColumnTree.superclass.render.apply(this);
19041         
19042         this.el.addClass('x-column-tree');
19043         
19044         this.headers = this.el.createChild(
19045             {cls:'x-tree-headers'},this.innerCt.dom);
19046    
19047         var cols = this.columns, c;
19048         var totalWidth = 0;
19049         this.headEls = [];
19050         var  len = cols.length;
19051         for(var i = 0; i < len; i++){
19052              c = cols[i];
19053              totalWidth += c.width;
19054             this.headEls.push(this.headers.createChild({
19055                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19056                  cn: {
19057                      cls:'x-tree-hd-text',
19058                      html: c.header
19059                  },
19060                  style:'width:'+(c.width-this.borderWidth)+'px;'
19061              }));
19062         }
19063         this.headers.createChild({cls:'x-clear'});
19064         // prevent floats from wrapping when clipped
19065         this.headers.setWidth(totalWidth);
19066         //this.innerCt.setWidth(totalWidth);
19067         this.innerCt.setStyle({ overflow: 'auto' });
19068         this.onResize(this.width, this.height);
19069              
19070         
19071     },
19072     onResize : function(w,h)
19073     {
19074         this.height = h;
19075         this.width = w;
19076         // resize cols..
19077         this.innerCt.setWidth(this.width);
19078         this.innerCt.setHeight(this.height-20);
19079         
19080         // headers...
19081         var cols = this.columns, c;
19082         var totalWidth = 0;
19083         var expEl = false;
19084         var len = cols.length;
19085         for(var i = 0; i < len; i++){
19086             c = cols[i];
19087             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19088                 // it's the expander..
19089                 expEl  = this.headEls[i];
19090                 continue;
19091             }
19092             totalWidth += c.width;
19093             
19094         }
19095         if (expEl) {
19096             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19097         }
19098         this.headers.setWidth(w-20);
19099
19100         
19101         
19102         
19103     }
19104 });
19105 /*
19106  * Based on:
19107  * Ext JS Library 1.1.1
19108  * Copyright(c) 2006-2007, Ext JS, LLC.
19109  *
19110  * Originally Released Under LGPL - original licence link has changed is not relivant.
19111  *
19112  * Fork - LGPL
19113  * <script type="text/javascript">
19114  */
19115  
19116 /**
19117  * @class Roo.menu.Menu
19118  * @extends Roo.util.Observable
19119  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19120  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19121  * @constructor
19122  * Creates a new Menu
19123  * @param {Object} config Configuration options
19124  */
19125 Roo.menu.Menu = function(config){
19126     Roo.apply(this, config);
19127     this.id = this.id || Roo.id();
19128     this.addEvents({
19129         /**
19130          * @event beforeshow
19131          * Fires before this menu is displayed
19132          * @param {Roo.menu.Menu} this
19133          */
19134         beforeshow : true,
19135         /**
19136          * @event beforehide
19137          * Fires before this menu is hidden
19138          * @param {Roo.menu.Menu} this
19139          */
19140         beforehide : true,
19141         /**
19142          * @event show
19143          * Fires after this menu is displayed
19144          * @param {Roo.menu.Menu} this
19145          */
19146         show : true,
19147         /**
19148          * @event hide
19149          * Fires after this menu is hidden
19150          * @param {Roo.menu.Menu} this
19151          */
19152         hide : true,
19153         /**
19154          * @event click
19155          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19156          * @param {Roo.menu.Menu} this
19157          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19158          * @param {Roo.EventObject} e
19159          */
19160         click : true,
19161         /**
19162          * @event mouseover
19163          * Fires when the mouse is hovering over this menu
19164          * @param {Roo.menu.Menu} this
19165          * @param {Roo.EventObject} e
19166          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19167          */
19168         mouseover : true,
19169         /**
19170          * @event mouseout
19171          * Fires when the mouse exits this menu
19172          * @param {Roo.menu.Menu} this
19173          * @param {Roo.EventObject} e
19174          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19175          */
19176         mouseout : true,
19177         /**
19178          * @event itemclick
19179          * Fires when a menu item contained in this menu is clicked
19180          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19181          * @param {Roo.EventObject} e
19182          */
19183         itemclick: true
19184     });
19185     if (this.registerMenu) {
19186         Roo.menu.MenuMgr.register(this);
19187     }
19188     
19189     var mis = this.items;
19190     this.items = new Roo.util.MixedCollection();
19191     if(mis){
19192         this.add.apply(this, mis);
19193     }
19194 };
19195
19196 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19197     /**
19198      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19199      */
19200     minWidth : 120,
19201     /**
19202      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19203      * for bottom-right shadow (defaults to "sides")
19204      */
19205     shadow : "sides",
19206     /**
19207      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19208      * this menu (defaults to "tl-tr?")
19209      */
19210     subMenuAlign : "tl-tr?",
19211     /**
19212      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19213      * relative to its element of origin (defaults to "tl-bl?")
19214      */
19215     defaultAlign : "tl-bl?",
19216     /**
19217      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19218      */
19219     allowOtherMenus : false,
19220     /**
19221      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19222      */
19223     registerMenu : true,
19224
19225     hidden:true,
19226
19227     // private
19228     render : function(){
19229         if(this.el){
19230             return;
19231         }
19232         var el = this.el = new Roo.Layer({
19233             cls: "x-menu",
19234             shadow:this.shadow,
19235             constrain: false,
19236             parentEl: this.parentEl || document.body,
19237             zindex:15000
19238         });
19239
19240         this.keyNav = new Roo.menu.MenuNav(this);
19241
19242         if(this.plain){
19243             el.addClass("x-menu-plain");
19244         }
19245         if(this.cls){
19246             el.addClass(this.cls);
19247         }
19248         // generic focus element
19249         this.focusEl = el.createChild({
19250             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19251         });
19252         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19253         ul.on("click", this.onClick, this);
19254         ul.on("mouseover", this.onMouseOver, this);
19255         ul.on("mouseout", this.onMouseOut, this);
19256         this.items.each(function(item){
19257             var li = document.createElement("li");
19258             li.className = "x-menu-list-item";
19259             ul.dom.appendChild(li);
19260             item.render(li, this);
19261         }, this);
19262         this.ul = ul;
19263         this.autoWidth();
19264     },
19265
19266     // private
19267     autoWidth : function(){
19268         var el = this.el, ul = this.ul;
19269         if(!el){
19270             return;
19271         }
19272         var w = this.width;
19273         if(w){
19274             el.setWidth(w);
19275         }else if(Roo.isIE){
19276             el.setWidth(this.minWidth);
19277             var t = el.dom.offsetWidth; // force recalc
19278             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19279         }
19280     },
19281
19282     // private
19283     delayAutoWidth : function(){
19284         if(this.rendered){
19285             if(!this.awTask){
19286                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19287             }
19288             this.awTask.delay(20);
19289         }
19290     },
19291
19292     // private
19293     findTargetItem : function(e){
19294         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19295         if(t && t.menuItemId){
19296             return this.items.get(t.menuItemId);
19297         }
19298     },
19299
19300     // private
19301     onClick : function(e){
19302         var t;
19303         if(t = this.findTargetItem(e)){
19304             t.onClick(e);
19305             this.fireEvent("click", this, t, e);
19306         }
19307     },
19308
19309     // private
19310     setActiveItem : function(item, autoExpand){
19311         if(item != this.activeItem){
19312             if(this.activeItem){
19313                 this.activeItem.deactivate();
19314             }
19315             this.activeItem = item;
19316             item.activate(autoExpand);
19317         }else if(autoExpand){
19318             item.expandMenu();
19319         }
19320     },
19321
19322     // private
19323     tryActivate : function(start, step){
19324         var items = this.items;
19325         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19326             var item = items.get(i);
19327             if(!item.disabled && item.canActivate){
19328                 this.setActiveItem(item, false);
19329                 return item;
19330             }
19331         }
19332         return false;
19333     },
19334
19335     // private
19336     onMouseOver : function(e){
19337         var t;
19338         if(t = this.findTargetItem(e)){
19339             if(t.canActivate && !t.disabled){
19340                 this.setActiveItem(t, true);
19341             }
19342         }
19343         this.fireEvent("mouseover", this, e, t);
19344     },
19345
19346     // private
19347     onMouseOut : function(e){
19348         var t;
19349         if(t = this.findTargetItem(e)){
19350             if(t == this.activeItem && t.shouldDeactivate(e)){
19351                 this.activeItem.deactivate();
19352                 delete this.activeItem;
19353             }
19354         }
19355         this.fireEvent("mouseout", this, e, t);
19356     },
19357
19358     /**
19359      * Read-only.  Returns true if the menu is currently displayed, else false.
19360      * @type Boolean
19361      */
19362     isVisible : function(){
19363         return this.el && !this.hidden;
19364     },
19365
19366     /**
19367      * Displays this menu relative to another element
19368      * @param {String/HTMLElement/Roo.Element} element The element to align to
19369      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19370      * the element (defaults to this.defaultAlign)
19371      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19372      */
19373     show : function(el, pos, parentMenu){
19374         this.parentMenu = parentMenu;
19375         if(!this.el){
19376             this.render();
19377         }
19378         this.fireEvent("beforeshow", this);
19379         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19380     },
19381
19382     /**
19383      * Displays this menu at a specific xy position
19384      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19385      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19386      */
19387     showAt : function(xy, parentMenu, /* private: */_e){
19388         this.parentMenu = parentMenu;
19389         if(!this.el){
19390             this.render();
19391         }
19392         if(_e !== false){
19393             this.fireEvent("beforeshow", this);
19394             xy = this.el.adjustForConstraints(xy);
19395         }
19396         this.el.setXY(xy);
19397         this.el.show();
19398         this.hidden = false;
19399         this.focus();
19400         this.fireEvent("show", this);
19401     },
19402
19403     focus : function(){
19404         if(!this.hidden){
19405             this.doFocus.defer(50, this);
19406         }
19407     },
19408
19409     doFocus : function(){
19410         if(!this.hidden){
19411             this.focusEl.focus();
19412         }
19413     },
19414
19415     /**
19416      * Hides this menu and optionally all parent menus
19417      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19418      */
19419     hide : function(deep){
19420         if(this.el && this.isVisible()){
19421             this.fireEvent("beforehide", this);
19422             if(this.activeItem){
19423                 this.activeItem.deactivate();
19424                 this.activeItem = null;
19425             }
19426             this.el.hide();
19427             this.hidden = true;
19428             this.fireEvent("hide", this);
19429         }
19430         if(deep === true && this.parentMenu){
19431             this.parentMenu.hide(true);
19432         }
19433     },
19434
19435     /**
19436      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19437      * Any of the following are valid:
19438      * <ul>
19439      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19440      * <li>An HTMLElement object which will be converted to a menu item</li>
19441      * <li>A menu item config object that will be created as a new menu item</li>
19442      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19443      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19444      * </ul>
19445      * Usage:
19446      * <pre><code>
19447 // Create the menu
19448 var menu = new Roo.menu.Menu();
19449
19450 // Create a menu item to add by reference
19451 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19452
19453 // Add a bunch of items at once using different methods.
19454 // Only the last item added will be returned.
19455 var item = menu.add(
19456     menuItem,                // add existing item by ref
19457     'Dynamic Item',          // new TextItem
19458     '-',                     // new separator
19459     { text: 'Config Item' }  // new item by config
19460 );
19461 </code></pre>
19462      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19463      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19464      */
19465     add : function(){
19466         var a = arguments, l = a.length, item;
19467         for(var i = 0; i < l; i++){
19468             var el = a[i];
19469             if(el.render){ // some kind of Item
19470                 item = this.addItem(el);
19471             }else if(typeof el == "string"){ // string
19472                 if(el == "separator" || el == "-"){
19473                     item = this.addSeparator();
19474                 }else{
19475                     item = this.addText(el);
19476                 }
19477             }else if(el.tagName || el.el){ // element
19478                 item = this.addElement(el);
19479             }else if(typeof el == "object"){ // must be menu item config?
19480                 item = this.addMenuItem(el);
19481             }
19482         }
19483         return item;
19484     },
19485
19486     /**
19487      * Returns this menu's underlying {@link Roo.Element} object
19488      * @return {Roo.Element} The element
19489      */
19490     getEl : function(){
19491         if(!this.el){
19492             this.render();
19493         }
19494         return this.el;
19495     },
19496
19497     /**
19498      * Adds a separator bar to the menu
19499      * @return {Roo.menu.Item} The menu item that was added
19500      */
19501     addSeparator : function(){
19502         return this.addItem(new Roo.menu.Separator());
19503     },
19504
19505     /**
19506      * Adds an {@link Roo.Element} object to the menu
19507      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19508      * @return {Roo.menu.Item} The menu item that was added
19509      */
19510     addElement : function(el){
19511         return this.addItem(new Roo.menu.BaseItem(el));
19512     },
19513
19514     /**
19515      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19516      * @param {Roo.menu.Item} item The menu item to add
19517      * @return {Roo.menu.Item} The menu item that was added
19518      */
19519     addItem : function(item){
19520         this.items.add(item);
19521         if(this.ul){
19522             var li = document.createElement("li");
19523             li.className = "x-menu-list-item";
19524             this.ul.dom.appendChild(li);
19525             item.render(li, this);
19526             this.delayAutoWidth();
19527         }
19528         return item;
19529     },
19530
19531     /**
19532      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19533      * @param {Object} config A MenuItem config object
19534      * @return {Roo.menu.Item} The menu item that was added
19535      */
19536     addMenuItem : function(config){
19537         if(!(config instanceof Roo.menu.Item)){
19538             if(typeof config.checked == "boolean"){ // must be check menu item config?
19539                 config = new Roo.menu.CheckItem(config);
19540             }else{
19541                 config = new Roo.menu.Item(config);
19542             }
19543         }
19544         return this.addItem(config);
19545     },
19546
19547     /**
19548      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19549      * @param {String} text The text to display in the menu item
19550      * @return {Roo.menu.Item} The menu item that was added
19551      */
19552     addText : function(text){
19553         return this.addItem(new Roo.menu.TextItem(text));
19554     },
19555
19556     /**
19557      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19558      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19559      * @param {Roo.menu.Item} item The menu item to add
19560      * @return {Roo.menu.Item} The menu item that was added
19561      */
19562     insert : function(index, item){
19563         this.items.insert(index, item);
19564         if(this.ul){
19565             var li = document.createElement("li");
19566             li.className = "x-menu-list-item";
19567             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19568             item.render(li, this);
19569             this.delayAutoWidth();
19570         }
19571         return item;
19572     },
19573
19574     /**
19575      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19576      * @param {Roo.menu.Item} item The menu item to remove
19577      */
19578     remove : function(item){
19579         this.items.removeKey(item.id);
19580         item.destroy();
19581     },
19582
19583     /**
19584      * Removes and destroys all items in the menu
19585      */
19586     removeAll : function(){
19587         var f;
19588         while(f = this.items.first()){
19589             this.remove(f);
19590         }
19591     }
19592 });
19593
19594 // MenuNav is a private utility class used internally by the Menu
19595 Roo.menu.MenuNav = function(menu){
19596     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19597     this.scope = this.menu = menu;
19598 };
19599
19600 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19601     doRelay : function(e, h){
19602         var k = e.getKey();
19603         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19604             this.menu.tryActivate(0, 1);
19605             return false;
19606         }
19607         return h.call(this.scope || this, e, this.menu);
19608     },
19609
19610     up : function(e, m){
19611         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19612             m.tryActivate(m.items.length-1, -1);
19613         }
19614     },
19615
19616     down : function(e, m){
19617         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19618             m.tryActivate(0, 1);
19619         }
19620     },
19621
19622     right : function(e, m){
19623         if(m.activeItem){
19624             m.activeItem.expandMenu(true);
19625         }
19626     },
19627
19628     left : function(e, m){
19629         m.hide();
19630         if(m.parentMenu && m.parentMenu.activeItem){
19631             m.parentMenu.activeItem.activate();
19632         }
19633     },
19634
19635     enter : function(e, m){
19636         if(m.activeItem){
19637             e.stopPropagation();
19638             m.activeItem.onClick(e);
19639             m.fireEvent("click", this, m.activeItem);
19640             return true;
19641         }
19642     }
19643 });/*
19644  * Based on:
19645  * Ext JS Library 1.1.1
19646  * Copyright(c) 2006-2007, Ext JS, LLC.
19647  *
19648  * Originally Released Under LGPL - original licence link has changed is not relivant.
19649  *
19650  * Fork - LGPL
19651  * <script type="text/javascript">
19652  */
19653  
19654 /**
19655  * @class Roo.menu.MenuMgr
19656  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19657  * @singleton
19658  */
19659 Roo.menu.MenuMgr = function(){
19660    var menus, active, groups = {}, attached = false, lastShow = new Date();
19661
19662    // private - called when first menu is created
19663    function init(){
19664        menus = {};
19665        active = new Roo.util.MixedCollection();
19666        Roo.get(document).addKeyListener(27, function(){
19667            if(active.length > 0){
19668                hideAll();
19669            }
19670        });
19671    }
19672
19673    // private
19674    function hideAll(){
19675        if(active && active.length > 0){
19676            var c = active.clone();
19677            c.each(function(m){
19678                m.hide();
19679            });
19680        }
19681    }
19682
19683    // private
19684    function onHide(m){
19685        active.remove(m);
19686        if(active.length < 1){
19687            Roo.get(document).un("mousedown", onMouseDown);
19688            attached = false;
19689        }
19690    }
19691
19692    // private
19693    function onShow(m){
19694        var last = active.last();
19695        lastShow = new Date();
19696        active.add(m);
19697        if(!attached){
19698            Roo.get(document).on("mousedown", onMouseDown);
19699            attached = true;
19700        }
19701        if(m.parentMenu){
19702           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19703           m.parentMenu.activeChild = m;
19704        }else if(last && last.isVisible()){
19705           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19706        }
19707    }
19708
19709    // private
19710    function onBeforeHide(m){
19711        if(m.activeChild){
19712            m.activeChild.hide();
19713        }
19714        if(m.autoHideTimer){
19715            clearTimeout(m.autoHideTimer);
19716            delete m.autoHideTimer;
19717        }
19718    }
19719
19720    // private
19721    function onBeforeShow(m){
19722        var pm = m.parentMenu;
19723        if(!pm && !m.allowOtherMenus){
19724            hideAll();
19725        }else if(pm && pm.activeChild && active != m){
19726            pm.activeChild.hide();
19727        }
19728    }
19729
19730    // private
19731    function onMouseDown(e){
19732        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19733            hideAll();
19734        }
19735    }
19736
19737    // private
19738    function onBeforeCheck(mi, state){
19739        if(state){
19740            var g = groups[mi.group];
19741            for(var i = 0, l = g.length; i < l; i++){
19742                if(g[i] != mi){
19743                    g[i].setChecked(false);
19744                }
19745            }
19746        }
19747    }
19748
19749    return {
19750
19751        /**
19752         * Hides all menus that are currently visible
19753         */
19754        hideAll : function(){
19755             hideAll();  
19756        },
19757
19758        // private
19759        register : function(menu){
19760            if(!menus){
19761                init();
19762            }
19763            menus[menu.id] = menu;
19764            menu.on("beforehide", onBeforeHide);
19765            menu.on("hide", onHide);
19766            menu.on("beforeshow", onBeforeShow);
19767            menu.on("show", onShow);
19768            var g = menu.group;
19769            if(g && menu.events["checkchange"]){
19770                if(!groups[g]){
19771                    groups[g] = [];
19772                }
19773                groups[g].push(menu);
19774                menu.on("checkchange", onCheck);
19775            }
19776        },
19777
19778         /**
19779          * Returns a {@link Roo.menu.Menu} object
19780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19781          * be used to generate and return a new Menu instance.
19782          */
19783        get : function(menu){
19784            if(typeof menu == "string"){ // menu id
19785                return menus[menu];
19786            }else if(menu.events){  // menu instance
19787                return menu;
19788            }else if(typeof menu.length == 'number'){ // array of menu items?
19789                return new Roo.menu.Menu({items:menu});
19790            }else{ // otherwise, must be a config
19791                return new Roo.menu.Menu(menu);
19792            }
19793        },
19794
19795        // private
19796        unregister : function(menu){
19797            delete menus[menu.id];
19798            menu.un("beforehide", onBeforeHide);
19799            menu.un("hide", onHide);
19800            menu.un("beforeshow", onBeforeShow);
19801            menu.un("show", onShow);
19802            var g = menu.group;
19803            if(g && menu.events["checkchange"]){
19804                groups[g].remove(menu);
19805                menu.un("checkchange", onCheck);
19806            }
19807        },
19808
19809        // private
19810        registerCheckable : function(menuItem){
19811            var g = menuItem.group;
19812            if(g){
19813                if(!groups[g]){
19814                    groups[g] = [];
19815                }
19816                groups[g].push(menuItem);
19817                menuItem.on("beforecheckchange", onBeforeCheck);
19818            }
19819        },
19820
19821        // private
19822        unregisterCheckable : function(menuItem){
19823            var g = menuItem.group;
19824            if(g){
19825                groups[g].remove(menuItem);
19826                menuItem.un("beforecheckchange", onBeforeCheck);
19827            }
19828        }
19829    };
19830 }();/*
19831  * Based on:
19832  * Ext JS Library 1.1.1
19833  * Copyright(c) 2006-2007, Ext JS, LLC.
19834  *
19835  * Originally Released Under LGPL - original licence link has changed is not relivant.
19836  *
19837  * Fork - LGPL
19838  * <script type="text/javascript">
19839  */
19840  
19841
19842 /**
19843  * @class Roo.menu.BaseItem
19844  * @extends Roo.Component
19845  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19846  * management and base configuration options shared by all menu components.
19847  * @constructor
19848  * Creates a new BaseItem
19849  * @param {Object} config Configuration options
19850  */
19851 Roo.menu.BaseItem = function(config){
19852     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19853
19854     this.addEvents({
19855         /**
19856          * @event click
19857          * Fires when this item is clicked
19858          * @param {Roo.menu.BaseItem} this
19859          * @param {Roo.EventObject} e
19860          */
19861         click: true,
19862         /**
19863          * @event activate
19864          * Fires when this item is activated
19865          * @param {Roo.menu.BaseItem} this
19866          */
19867         activate : true,
19868         /**
19869          * @event deactivate
19870          * Fires when this item is deactivated
19871          * @param {Roo.menu.BaseItem} this
19872          */
19873         deactivate : true
19874     });
19875
19876     if(this.handler){
19877         this.on("click", this.handler, this.scope, true);
19878     }
19879 };
19880
19881 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19882     /**
19883      * @cfg {Function} handler
19884      * A function that will handle the click event of this menu item (defaults to undefined)
19885      */
19886     /**
19887      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19888      */
19889     canActivate : false,
19890     /**
19891      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19892      */
19893     activeClass : "x-menu-item-active",
19894     /**
19895      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19896      */
19897     hideOnClick : true,
19898     /**
19899      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19900      */
19901     hideDelay : 100,
19902
19903     // private
19904     ctype: "Roo.menu.BaseItem",
19905
19906     // private
19907     actionMode : "container",
19908
19909     // private
19910     render : function(container, parentMenu){
19911         this.parentMenu = parentMenu;
19912         Roo.menu.BaseItem.superclass.render.call(this, container);
19913         this.container.menuItemId = this.id;
19914     },
19915
19916     // private
19917     onRender : function(container, position){
19918         this.el = Roo.get(this.el);
19919         container.dom.appendChild(this.el.dom);
19920     },
19921
19922     // private
19923     onClick : function(e){
19924         if(!this.disabled && this.fireEvent("click", this, e) !== false
19925                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19926             this.handleClick(e);
19927         }else{
19928             e.stopEvent();
19929         }
19930     },
19931
19932     // private
19933     activate : function(){
19934         if(this.disabled){
19935             return false;
19936         }
19937         var li = this.container;
19938         li.addClass(this.activeClass);
19939         this.region = li.getRegion().adjust(2, 2, -2, -2);
19940         this.fireEvent("activate", this);
19941         return true;
19942     },
19943
19944     // private
19945     deactivate : function(){
19946         this.container.removeClass(this.activeClass);
19947         this.fireEvent("deactivate", this);
19948     },
19949
19950     // private
19951     shouldDeactivate : function(e){
19952         return !this.region || !this.region.contains(e.getPoint());
19953     },
19954
19955     // private
19956     handleClick : function(e){
19957         if(this.hideOnClick){
19958             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19959         }
19960     },
19961
19962     // private
19963     expandMenu : function(autoActivate){
19964         // do nothing
19965     },
19966
19967     // private
19968     hideMenu : function(){
19969         // do nothing
19970     }
19971 });/*
19972  * Based on:
19973  * Ext JS Library 1.1.1
19974  * Copyright(c) 2006-2007, Ext JS, LLC.
19975  *
19976  * Originally Released Under LGPL - original licence link has changed is not relivant.
19977  *
19978  * Fork - LGPL
19979  * <script type="text/javascript">
19980  */
19981  
19982 /**
19983  * @class Roo.menu.Adapter
19984  * @extends Roo.menu.BaseItem
19985  * 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.
19986  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
19987  * @constructor
19988  * Creates a new Adapter
19989  * @param {Object} config Configuration options
19990  */
19991 Roo.menu.Adapter = function(component, config){
19992     Roo.menu.Adapter.superclass.constructor.call(this, config);
19993     this.component = component;
19994 };
19995 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
19996     // private
19997     canActivate : true,
19998
19999     // private
20000     onRender : function(container, position){
20001         this.component.render(container);
20002         this.el = this.component.getEl();
20003     },
20004
20005     // private
20006     activate : function(){
20007         if(this.disabled){
20008             return false;
20009         }
20010         this.component.focus();
20011         this.fireEvent("activate", this);
20012         return true;
20013     },
20014
20015     // private
20016     deactivate : function(){
20017         this.fireEvent("deactivate", this);
20018     },
20019
20020     // private
20021     disable : function(){
20022         this.component.disable();
20023         Roo.menu.Adapter.superclass.disable.call(this);
20024     },
20025
20026     // private
20027     enable : function(){
20028         this.component.enable();
20029         Roo.menu.Adapter.superclass.enable.call(this);
20030     }
20031 });/*
20032  * Based on:
20033  * Ext JS Library 1.1.1
20034  * Copyright(c) 2006-2007, Ext JS, LLC.
20035  *
20036  * Originally Released Under LGPL - original licence link has changed is not relivant.
20037  *
20038  * Fork - LGPL
20039  * <script type="text/javascript">
20040  */
20041
20042 /**
20043  * @class Roo.menu.TextItem
20044  * @extends Roo.menu.BaseItem
20045  * Adds a static text string to a menu, usually used as either a heading or group separator.
20046  * @constructor
20047  * Creates a new TextItem
20048  * @param {String} text The text to display
20049  */
20050 Roo.menu.TextItem = function(text){
20051     this.text = text;
20052     Roo.menu.TextItem.superclass.constructor.call(this);
20053 };
20054
20055 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20056     /**
20057      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20058      */
20059     hideOnClick : false,
20060     /**
20061      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20062      */
20063     itemCls : "x-menu-text",
20064
20065     // private
20066     onRender : function(){
20067         var s = document.createElement("span");
20068         s.className = this.itemCls;
20069         s.innerHTML = this.text;
20070         this.el = s;
20071         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20072     }
20073 });/*
20074  * Based on:
20075  * Ext JS Library 1.1.1
20076  * Copyright(c) 2006-2007, Ext JS, LLC.
20077  *
20078  * Originally Released Under LGPL - original licence link has changed is not relivant.
20079  *
20080  * Fork - LGPL
20081  * <script type="text/javascript">
20082  */
20083
20084 /**
20085  * @class Roo.menu.Separator
20086  * @extends Roo.menu.BaseItem
20087  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20088  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20089  * @constructor
20090  * @param {Object} config Configuration options
20091  */
20092 Roo.menu.Separator = function(config){
20093     Roo.menu.Separator.superclass.constructor.call(this, config);
20094 };
20095
20096 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20097     /**
20098      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20099      */
20100     itemCls : "x-menu-sep",
20101     /**
20102      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20103      */
20104     hideOnClick : false,
20105
20106     // private
20107     onRender : function(li){
20108         var s = document.createElement("span");
20109         s.className = this.itemCls;
20110         s.innerHTML = "&#160;";
20111         this.el = s;
20112         li.addClass("x-menu-sep-li");
20113         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20114     }
20115 });/*
20116  * Based on:
20117  * Ext JS Library 1.1.1
20118  * Copyright(c) 2006-2007, Ext JS, LLC.
20119  *
20120  * Originally Released Under LGPL - original licence link has changed is not relivant.
20121  *
20122  * Fork - LGPL
20123  * <script type="text/javascript">
20124  */
20125 /**
20126  * @class Roo.menu.Item
20127  * @extends Roo.menu.BaseItem
20128  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20129  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20130  * activation and click handling.
20131  * @constructor
20132  * Creates a new Item
20133  * @param {Object} config Configuration options
20134  */
20135 Roo.menu.Item = function(config){
20136     Roo.menu.Item.superclass.constructor.call(this, config);
20137     if(this.menu){
20138         this.menu = Roo.menu.MenuMgr.get(this.menu);
20139     }
20140 };
20141 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20142     /**
20143      * @cfg {String} icon
20144      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20145      */
20146     /**
20147      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20148      */
20149     itemCls : "x-menu-item",
20150     /**
20151      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20152      */
20153     canActivate : true,
20154     /**
20155      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20156      */
20157     showDelay: 200,
20158     // doc'd in BaseItem
20159     hideDelay: 200,
20160
20161     // private
20162     ctype: "Roo.menu.Item",
20163     
20164     // private
20165     onRender : function(container, position){
20166         var el = document.createElement("a");
20167         el.hideFocus = true;
20168         el.unselectable = "on";
20169         el.href = this.href || "#";
20170         if(this.hrefTarget){
20171             el.target = this.hrefTarget;
20172         }
20173         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20174         el.innerHTML = String.format(
20175                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
20176                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
20177         this.el = el;
20178         Roo.menu.Item.superclass.onRender.call(this, container, position);
20179     },
20180
20181     /**
20182      * Sets the text to display in this menu item
20183      * @param {String} text The text to display
20184      */
20185     setText : function(text){
20186         this.text = text;
20187         if(this.rendered){
20188             this.el.update(String.format(
20189                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
20190                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20191             this.parentMenu.autoWidth();
20192         }
20193     },
20194
20195     // private
20196     handleClick : function(e){
20197         if(!this.href){ // if no link defined, stop the event automatically
20198             e.stopEvent();
20199         }
20200         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20201     },
20202
20203     // private
20204     activate : function(autoExpand){
20205         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20206             this.focus();
20207             if(autoExpand){
20208                 this.expandMenu();
20209             }
20210         }
20211         return true;
20212     },
20213
20214     // private
20215     shouldDeactivate : function(e){
20216         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20217             if(this.menu && this.menu.isVisible()){
20218                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20219             }
20220             return true;
20221         }
20222         return false;
20223     },
20224
20225     // private
20226     deactivate : function(){
20227         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20228         this.hideMenu();
20229     },
20230
20231     // private
20232     expandMenu : function(autoActivate){
20233         if(!this.disabled && this.menu){
20234             clearTimeout(this.hideTimer);
20235             delete this.hideTimer;
20236             if(!this.menu.isVisible() && !this.showTimer){
20237                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20238             }else if (this.menu.isVisible() && autoActivate){
20239                 this.menu.tryActivate(0, 1);
20240             }
20241         }
20242     },
20243
20244     // private
20245     deferExpand : function(autoActivate){
20246         delete this.showTimer;
20247         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20248         if(autoActivate){
20249             this.menu.tryActivate(0, 1);
20250         }
20251     },
20252
20253     // private
20254     hideMenu : function(){
20255         clearTimeout(this.showTimer);
20256         delete this.showTimer;
20257         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20258             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20259         }
20260     },
20261
20262     // private
20263     deferHide : function(){
20264         delete this.hideTimer;
20265         this.menu.hide();
20266     }
20267 });/*
20268  * Based on:
20269  * Ext JS Library 1.1.1
20270  * Copyright(c) 2006-2007, Ext JS, LLC.
20271  *
20272  * Originally Released Under LGPL - original licence link has changed is not relivant.
20273  *
20274  * Fork - LGPL
20275  * <script type="text/javascript">
20276  */
20277  
20278 /**
20279  * @class Roo.menu.CheckItem
20280  * @extends Roo.menu.Item
20281  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20282  * @constructor
20283  * Creates a new CheckItem
20284  * @param {Object} config Configuration options
20285  */
20286 Roo.menu.CheckItem = function(config){
20287     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20288     this.addEvents({
20289         /**
20290          * @event beforecheckchange
20291          * Fires before the checked value is set, providing an opportunity to cancel if needed
20292          * @param {Roo.menu.CheckItem} this
20293          * @param {Boolean} checked The new checked value that will be set
20294          */
20295         "beforecheckchange" : true,
20296         /**
20297          * @event checkchange
20298          * Fires after the checked value has been set
20299          * @param {Roo.menu.CheckItem} this
20300          * @param {Boolean} checked The checked value that was set
20301          */
20302         "checkchange" : true
20303     });
20304     if(this.checkHandler){
20305         this.on('checkchange', this.checkHandler, this.scope);
20306     }
20307 };
20308 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20309     /**
20310      * @cfg {String} group
20311      * All check items with the same group name will automatically be grouped into a single-select
20312      * radio button group (defaults to '')
20313      */
20314     /**
20315      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20316      */
20317     itemCls : "x-menu-item x-menu-check-item",
20318     /**
20319      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20320      */
20321     groupClass : "x-menu-group-item",
20322
20323     /**
20324      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20325      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20326      * initialized with checked = true will be rendered as checked.
20327      */
20328     checked: false,
20329
20330     // private
20331     ctype: "Roo.menu.CheckItem",
20332
20333     // private
20334     onRender : function(c){
20335         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20336         if(this.group){
20337             this.el.addClass(this.groupClass);
20338         }
20339         Roo.menu.MenuMgr.registerCheckable(this);
20340         if(this.checked){
20341             this.checked = false;
20342             this.setChecked(true, true);
20343         }
20344     },
20345
20346     // private
20347     destroy : function(){
20348         if(this.rendered){
20349             Roo.menu.MenuMgr.unregisterCheckable(this);
20350         }
20351         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20352     },
20353
20354     /**
20355      * Set the checked state of this item
20356      * @param {Boolean} checked The new checked value
20357      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20358      */
20359     setChecked : function(state, suppressEvent){
20360         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20361             if(this.container){
20362                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20363             }
20364             this.checked = state;
20365             if(suppressEvent !== true){
20366                 this.fireEvent("checkchange", this, state);
20367             }
20368         }
20369     },
20370
20371     // private
20372     handleClick : function(e){
20373        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20374            this.setChecked(!this.checked);
20375        }
20376        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20377     }
20378 });/*
20379  * Based on:
20380  * Ext JS Library 1.1.1
20381  * Copyright(c) 2006-2007, Ext JS, LLC.
20382  *
20383  * Originally Released Under LGPL - original licence link has changed is not relivant.
20384  *
20385  * Fork - LGPL
20386  * <script type="text/javascript">
20387  */
20388  
20389 /**
20390  * @class Roo.menu.DateItem
20391  * @extends Roo.menu.Adapter
20392  * A menu item that wraps the {@link Roo.DatPicker} component.
20393  * @constructor
20394  * Creates a new DateItem
20395  * @param {Object} config Configuration options
20396  */
20397 Roo.menu.DateItem = function(config){
20398     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20399     /** The Roo.DatePicker object @type Roo.DatePicker */
20400     this.picker = this.component;
20401     this.addEvents({select: true});
20402     
20403     this.picker.on("render", function(picker){
20404         picker.getEl().swallowEvent("click");
20405         picker.container.addClass("x-menu-date-item");
20406     });
20407
20408     this.picker.on("select", this.onSelect, this);
20409 };
20410
20411 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20412     // private
20413     onSelect : function(picker, date){
20414         this.fireEvent("select", this, date, picker);
20415         Roo.menu.DateItem.superclass.handleClick.call(this);
20416     }
20417 });/*
20418  * Based on:
20419  * Ext JS Library 1.1.1
20420  * Copyright(c) 2006-2007, Ext JS, LLC.
20421  *
20422  * Originally Released Under LGPL - original licence link has changed is not relivant.
20423  *
20424  * Fork - LGPL
20425  * <script type="text/javascript">
20426  */
20427  
20428 /**
20429  * @class Roo.menu.ColorItem
20430  * @extends Roo.menu.Adapter
20431  * A menu item that wraps the {@link Roo.ColorPalette} component.
20432  * @constructor
20433  * Creates a new ColorItem
20434  * @param {Object} config Configuration options
20435  */
20436 Roo.menu.ColorItem = function(config){
20437     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20438     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20439     this.palette = this.component;
20440     this.relayEvents(this.palette, ["select"]);
20441     if(this.selectHandler){
20442         this.on('select', this.selectHandler, this.scope);
20443     }
20444 };
20445 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
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 /**
20458  * @class Roo.menu.DateMenu
20459  * @extends Roo.menu.Menu
20460  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20461  * @constructor
20462  * Creates a new DateMenu
20463  * @param {Object} config Configuration options
20464  */
20465 Roo.menu.DateMenu = function(config){
20466     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20467     this.plain = true;
20468     var di = new Roo.menu.DateItem(config);
20469     this.add(di);
20470     /**
20471      * The {@link Roo.DatePicker} instance for this DateMenu
20472      * @type DatePicker
20473      */
20474     this.picker = di.picker;
20475     /**
20476      * @event select
20477      * @param {DatePicker} picker
20478      * @param {Date} date
20479      */
20480     this.relayEvents(di, ["select"]);
20481
20482     this.on('beforeshow', function(){
20483         if(this.picker){
20484             this.picker.hideMonthPicker(true);
20485         }
20486     }, this);
20487 };
20488 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20489     cls:'x-date-menu'
20490 });/*
20491  * Based on:
20492  * Ext JS Library 1.1.1
20493  * Copyright(c) 2006-2007, Ext JS, LLC.
20494  *
20495  * Originally Released Under LGPL - original licence link has changed is not relivant.
20496  *
20497  * Fork - LGPL
20498  * <script type="text/javascript">
20499  */
20500  
20501
20502 /**
20503  * @class Roo.menu.ColorMenu
20504  * @extends Roo.menu.Menu
20505  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20506  * @constructor
20507  * Creates a new ColorMenu
20508  * @param {Object} config Configuration options
20509  */
20510 Roo.menu.ColorMenu = function(config){
20511     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20512     this.plain = true;
20513     var ci = new Roo.menu.ColorItem(config);
20514     this.add(ci);
20515     /**
20516      * The {@link Roo.ColorPalette} instance for this ColorMenu
20517      * @type ColorPalette
20518      */
20519     this.palette = ci.palette;
20520     /**
20521      * @event select
20522      * @param {ColorPalette} palette
20523      * @param {String} color
20524      */
20525     this.relayEvents(ci, ["select"]);
20526 };
20527 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20528  * Based on:
20529  * Ext JS Library 1.1.1
20530  * Copyright(c) 2006-2007, Ext JS, LLC.
20531  *
20532  * Originally Released Under LGPL - original licence link has changed is not relivant.
20533  *
20534  * Fork - LGPL
20535  * <script type="text/javascript">
20536  */
20537  
20538 /**
20539  * @class Roo.form.Field
20540  * @extends Roo.BoxComponent
20541  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20542  * @constructor
20543  * Creates a new Field
20544  * @param {Object} config Configuration options
20545  */
20546 Roo.form.Field = function(config){
20547     Roo.form.Field.superclass.constructor.call(this, config);
20548 };
20549
20550 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20551     /**
20552      * @cfg {String} fieldLabel Label to use when rendering a form.
20553      */
20554        /**
20555      * @cfg {String} qtip Mouse over tip
20556      */
20557      
20558     /**
20559      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20560      */
20561     invalidClass : "x-form-invalid",
20562     /**
20563      * @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")
20564      */
20565     invalidText : "The value in this field is invalid",
20566     /**
20567      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20568      */
20569     focusClass : "x-form-focus",
20570     /**
20571      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20572       automatic validation (defaults to "keyup").
20573      */
20574     validationEvent : "keyup",
20575     /**
20576      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20577      */
20578     validateOnBlur : true,
20579     /**
20580      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20581      */
20582     validationDelay : 250,
20583     /**
20584      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20585      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20586      */
20587     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20588     /**
20589      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20590      */
20591     fieldClass : "x-form-field",
20592     /**
20593      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20594      *<pre>
20595 Value         Description
20596 -----------   ----------------------------------------------------------------------
20597 qtip          Display a quick tip when the user hovers over the field
20598 title         Display a default browser title attribute popup
20599 under         Add a block div beneath the field containing the error text
20600 side          Add an error icon to the right of the field with a popup on hover
20601 [element id]  Add the error text directly to the innerHTML of the specified element
20602 </pre>
20603      */
20604     msgTarget : 'qtip',
20605     /**
20606      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20607      */
20608     msgFx : 'normal',
20609
20610     /**
20611      * @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.
20612      */
20613     readOnly : false,
20614
20615     /**
20616      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20617      */
20618     disabled : false,
20619
20620     /**
20621      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20622      */
20623     inputType : undefined,
20624     
20625     /**
20626      * @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).
20627          */
20628         tabIndex : undefined,
20629         
20630     // private
20631     isFormField : true,
20632
20633     // private
20634     hasFocus : false,
20635     /**
20636      * @property {Roo.Element} fieldEl
20637      * Element Containing the rendered Field (with label etc.)
20638      */
20639     /**
20640      * @cfg {Mixed} value A value to initialize this field with.
20641      */
20642     value : undefined,
20643
20644     /**
20645      * @cfg {String} name The field's HTML name attribute.
20646      */
20647     /**
20648      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20649      */
20650
20651         // private ??
20652         initComponent : function(){
20653         Roo.form.Field.superclass.initComponent.call(this);
20654         this.addEvents({
20655             /**
20656              * @event focus
20657              * Fires when this field receives input focus.
20658              * @param {Roo.form.Field} this
20659              */
20660             focus : true,
20661             /**
20662              * @event blur
20663              * Fires when this field loses input focus.
20664              * @param {Roo.form.Field} this
20665              */
20666             blur : true,
20667             /**
20668              * @event specialkey
20669              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20670              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20671              * @param {Roo.form.Field} this
20672              * @param {Roo.EventObject} e The event object
20673              */
20674             specialkey : true,
20675             /**
20676              * @event change
20677              * Fires just before the field blurs if the field value has changed.
20678              * @param {Roo.form.Field} this
20679              * @param {Mixed} newValue The new value
20680              * @param {Mixed} oldValue The original value
20681              */
20682             change : true,
20683             /**
20684              * @event invalid
20685              * Fires after the field has been marked as invalid.
20686              * @param {Roo.form.Field} this
20687              * @param {String} msg The validation message
20688              */
20689             invalid : true,
20690             /**
20691              * @event valid
20692              * Fires after the field has been validated with no errors.
20693              * @param {Roo.form.Field} this
20694              */
20695             valid : true
20696         });
20697     },
20698
20699     /**
20700      * Returns the name attribute of the field if available
20701      * @return {String} name The field name
20702      */
20703     getName: function(){
20704          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20705     },
20706
20707     // private
20708     onRender : function(ct, position){
20709         Roo.form.Field.superclass.onRender.call(this, ct, position);
20710         if(!this.el){
20711             var cfg = this.getAutoCreate();
20712             if(!cfg.name){
20713                 cfg.name = this.name || this.id;
20714             }
20715             if(this.inputType){
20716                 cfg.type = this.inputType;
20717             }
20718             this.el = ct.createChild(cfg, position);
20719         }
20720         var type = this.el.dom.type;
20721         if(type){
20722             if(type == 'password'){
20723                 type = 'text';
20724             }
20725             this.el.addClass('x-form-'+type);
20726         }
20727         if(this.readOnly){
20728             this.el.dom.readOnly = true;
20729         }
20730         if(this.tabIndex !== undefined){
20731             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20732         }
20733
20734         this.el.addClass([this.fieldClass, this.cls]);
20735         this.initValue();
20736     },
20737
20738     /**
20739      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20740      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20741      * @return {Roo.form.Field} this
20742      */
20743     applyTo : function(target){
20744         this.allowDomMove = false;
20745         this.el = Roo.get(target);
20746         this.render(this.el.dom.parentNode);
20747         return this;
20748     },
20749
20750     // private
20751     initValue : function(){
20752         if(this.value !== undefined){
20753             this.setValue(this.value);
20754         }else if(this.el.dom.value.length > 0){
20755             this.setValue(this.el.dom.value);
20756         }
20757     },
20758
20759     /**
20760      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20761      */
20762     isDirty : function() {
20763         if(this.disabled) {
20764             return false;
20765         }
20766         return String(this.getValue()) !== String(this.originalValue);
20767     },
20768
20769     // private
20770     afterRender : function(){
20771         Roo.form.Field.superclass.afterRender.call(this);
20772         this.initEvents();
20773     },
20774
20775     // private
20776     fireKey : function(e){
20777         if(e.isNavKeyPress()){
20778             this.fireEvent("specialkey", this, e);
20779         }
20780     },
20781
20782     /**
20783      * Resets the current field value to the originally loaded value and clears any validation messages
20784      */
20785     reset : function(){
20786         this.setValue(this.originalValue);
20787         this.clearInvalid();
20788     },
20789
20790     // private
20791     initEvents : function(){
20792         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
20793         this.el.on("focus", this.onFocus,  this);
20794         this.el.on("blur", this.onBlur,  this);
20795
20796         // reference to original value for reset
20797         this.originalValue = this.getValue();
20798     },
20799
20800     // private
20801     onFocus : function(){
20802         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20803             this.el.addClass(this.focusClass);
20804         }
20805         if(!this.hasFocus){
20806             this.hasFocus = true;
20807             this.startValue = this.getValue();
20808             this.fireEvent("focus", this);
20809         }
20810     },
20811
20812     beforeBlur : Roo.emptyFn,
20813
20814     // private
20815     onBlur : function(){
20816         this.beforeBlur();
20817         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20818             this.el.removeClass(this.focusClass);
20819         }
20820         this.hasFocus = false;
20821         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20822             this.validate();
20823         }
20824         var v = this.getValue();
20825         if(String(v) !== String(this.startValue)){
20826             this.fireEvent('change', this, v, this.startValue);
20827         }
20828         this.fireEvent("blur", this);
20829     },
20830
20831     /**
20832      * Returns whether or not the field value is currently valid
20833      * @param {Boolean} preventMark True to disable marking the field invalid
20834      * @return {Boolean} True if the value is valid, else false
20835      */
20836     isValid : function(preventMark){
20837         if(this.disabled){
20838             return true;
20839         }
20840         var restore = this.preventMark;
20841         this.preventMark = preventMark === true;
20842         var v = this.validateValue(this.processValue(this.getRawValue()));
20843         this.preventMark = restore;
20844         return v;
20845     },
20846
20847     /**
20848      * Validates the field value
20849      * @return {Boolean} True if the value is valid, else false
20850      */
20851     validate : function(){
20852         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20853             this.clearInvalid();
20854             return true;
20855         }
20856         return false;
20857     },
20858
20859     processValue : function(value){
20860         return value;
20861     },
20862
20863     // private
20864     // Subclasses should provide the validation implementation by overriding this
20865     validateValue : function(value){
20866         return true;
20867     },
20868
20869     /**
20870      * Mark this field as invalid
20871      * @param {String} msg The validation message
20872      */
20873     markInvalid : function(msg){
20874         if(!this.rendered || this.preventMark){ // not rendered
20875             return;
20876         }
20877         this.el.addClass(this.invalidClass);
20878         msg = msg || this.invalidText;
20879         switch(this.msgTarget){
20880             case 'qtip':
20881                 this.el.dom.qtip = msg;
20882                 this.el.dom.qclass = 'x-form-invalid-tip';
20883                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20884                     Roo.QuickTips.enable();
20885                 }
20886                 break;
20887             case 'title':
20888                 this.el.dom.title = msg;
20889                 break;
20890             case 'under':
20891                 if(!this.errorEl){
20892                     var elp = this.el.findParent('.x-form-element', 5, true);
20893                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20894                     this.errorEl.setWidth(elp.getWidth(true)-20);
20895                 }
20896                 this.errorEl.update(msg);
20897                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20898                 break;
20899             case 'side':
20900                 if(!this.errorIcon){
20901                     var elp = this.el.findParent('.x-form-element', 5, true);
20902                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20903                 }
20904                 this.alignErrorIcon();
20905                 this.errorIcon.dom.qtip = msg;
20906                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20907                 this.errorIcon.show();
20908                 this.on('resize', this.alignErrorIcon, this);
20909                 break;
20910             default:
20911                 var t = Roo.getDom(this.msgTarget);
20912                 t.innerHTML = msg;
20913                 t.style.display = this.msgDisplay;
20914                 break;
20915         }
20916         this.fireEvent('invalid', this, msg);
20917     },
20918
20919     // private
20920     alignErrorIcon : function(){
20921         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20922     },
20923
20924     /**
20925      * Clear any invalid styles/messages for this field
20926      */
20927     clearInvalid : function(){
20928         if(!this.rendered || this.preventMark){ // not rendered
20929             return;
20930         }
20931         this.el.removeClass(this.invalidClass);
20932         switch(this.msgTarget){
20933             case 'qtip':
20934                 this.el.dom.qtip = '';
20935                 break;
20936             case 'title':
20937                 this.el.dom.title = '';
20938                 break;
20939             case 'under':
20940                 if(this.errorEl){
20941                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
20942                 }
20943                 break;
20944             case 'side':
20945                 if(this.errorIcon){
20946                     this.errorIcon.dom.qtip = '';
20947                     this.errorIcon.hide();
20948                     this.un('resize', this.alignErrorIcon, this);
20949                 }
20950                 break;
20951             default:
20952                 var t = Roo.getDom(this.msgTarget);
20953                 t.innerHTML = '';
20954                 t.style.display = 'none';
20955                 break;
20956         }
20957         this.fireEvent('valid', this);
20958     },
20959
20960     /**
20961      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
20962      * @return {Mixed} value The field value
20963      */
20964     getRawValue : function(){
20965         var v = this.el.getValue();
20966         if(v === this.emptyText){
20967             v = '';
20968         }
20969         return v;
20970     },
20971
20972     /**
20973      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20974      * @return {Mixed} value The field value
20975      */
20976     getValue : function(){
20977         var v = this.el.getValue();
20978         if(v === this.emptyText || v === undefined){
20979             v = '';
20980         }
20981         return v;
20982     },
20983
20984     /**
20985      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
20986      * @param {Mixed} value The value to set
20987      */
20988     setRawValue : function(v){
20989         return this.el.dom.value = (v === null || v === undefined ? '' : v);
20990     },
20991
20992     /**
20993      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
20994      * @param {Mixed} value The value to set
20995      */
20996     setValue : function(v){
20997         this.value = v;
20998         if(this.rendered){
20999             this.el.dom.value = (v === null || v === undefined ? '' : v);
21000             this.validate();
21001         }
21002     },
21003
21004     adjustSize : function(w, h){
21005         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21006         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21007         return s;
21008     },
21009
21010     adjustWidth : function(tag, w){
21011         tag = tag.toLowerCase();
21012         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21013             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21014                 if(tag == 'input'){
21015                     return w + 2;
21016                 }
21017                 if(tag = 'textarea'){
21018                     return w-2;
21019                 }
21020             }else if(Roo.isOpera){
21021                 if(tag == 'input'){
21022                     return w + 2;
21023                 }
21024                 if(tag = 'textarea'){
21025                     return w-2;
21026                 }
21027             }
21028         }
21029         return w;
21030     }
21031 });
21032
21033
21034 // anything other than normal should be considered experimental
21035 Roo.form.Field.msgFx = {
21036     normal : {
21037         show: function(msgEl, f){
21038             msgEl.setDisplayed('block');
21039         },
21040
21041         hide : function(msgEl, f){
21042             msgEl.setDisplayed(false).update('');
21043         }
21044     },
21045
21046     slide : {
21047         show: function(msgEl, f){
21048             msgEl.slideIn('t', {stopFx:true});
21049         },
21050
21051         hide : function(msgEl, f){
21052             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21053         }
21054     },
21055
21056     slideRight : {
21057         show: function(msgEl, f){
21058             msgEl.fixDisplay();
21059             msgEl.alignTo(f.el, 'tl-tr');
21060             msgEl.slideIn('l', {stopFx:true});
21061         },
21062
21063         hide : function(msgEl, f){
21064             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21065         }
21066     }
21067 };/*
21068  * Based on:
21069  * Ext JS Library 1.1.1
21070  * Copyright(c) 2006-2007, Ext JS, LLC.
21071  *
21072  * Originally Released Under LGPL - original licence link has changed is not relivant.
21073  *
21074  * Fork - LGPL
21075  * <script type="text/javascript">
21076  */
21077  
21078
21079 /**
21080  * @class Roo.form.TextField
21081  * @extends Roo.form.Field
21082  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21083  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21084  * @constructor
21085  * Creates a new TextField
21086  * @param {Object} config Configuration options
21087  */
21088 Roo.form.TextField = function(config){
21089     Roo.form.TextField.superclass.constructor.call(this, config);
21090     this.addEvents({
21091         /**
21092          * @event autosize
21093          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21094          * according to the default logic, but this event provides a hook for the developer to apply additional
21095          * logic at runtime to resize the field if needed.
21096              * @param {Roo.form.Field} this This text field
21097              * @param {Number} width The new field width
21098              */
21099         autosize : true
21100     });
21101 };
21102
21103 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21104     /**
21105      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21106      */
21107     grow : false,
21108     /**
21109      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21110      */
21111     growMin : 30,
21112     /**
21113      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21114      */
21115     growMax : 800,
21116     /**
21117      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21118      */
21119     vtype : null,
21120     /**
21121      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21122      */
21123     maskRe : null,
21124     /**
21125      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21126      */
21127     disableKeyFilter : false,
21128     /**
21129      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21130      */
21131     allowBlank : true,
21132     /**
21133      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21134      */
21135     minLength : 0,
21136     /**
21137      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21138      */
21139     maxLength : Number.MAX_VALUE,
21140     /**
21141      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21142      */
21143     minLengthText : "The minimum length for this field is {0}",
21144     /**
21145      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21146      */
21147     maxLengthText : "The maximum length for this field is {0}",
21148     /**
21149      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21150      */
21151     selectOnFocus : false,
21152     /**
21153      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21154      */
21155     blankText : "This field is required",
21156     /**
21157      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21158      * If available, this function will be called only after the basic validators all return true, and will be passed the
21159      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21160      */
21161     validator : null,
21162     /**
21163      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21164      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21165      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21166      */
21167     regex : null,
21168     /**
21169      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21170      */
21171     regexText : "",
21172     /**
21173      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21174      */
21175     emptyText : null,
21176     /**
21177      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21178      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21179      */
21180     emptyClass : 'x-form-empty-field',
21181
21182     // private
21183     initEvents : function(){
21184         Roo.form.TextField.superclass.initEvents.call(this);
21185         if(this.validationEvent == 'keyup'){
21186             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21187             this.el.on('keyup', this.filterValidation, this);
21188         }
21189         else if(this.validationEvent !== false){
21190             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21191         }
21192         if(this.selectOnFocus || this.emptyText){
21193             this.on("focus", this.preFocus, this);
21194             if(this.emptyText){
21195                 this.on('blur', this.postBlur, this);
21196                 this.applyEmptyText();
21197             }
21198         }
21199         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21200             this.el.on("keypress", this.filterKeys, this);
21201         }
21202         if(this.grow){
21203             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21204             this.el.on("click", this.autoSize,  this);
21205         }
21206     },
21207
21208     processValue : function(value){
21209         if(this.stripCharsRe){
21210             var newValue = value.replace(this.stripCharsRe, '');
21211             if(newValue !== value){
21212                 this.setRawValue(newValue);
21213                 return newValue;
21214             }
21215         }
21216         return value;
21217     },
21218
21219     filterValidation : function(e){
21220         if(!e.isNavKeyPress()){
21221             this.validationTask.delay(this.validationDelay);
21222         }
21223     },
21224
21225     // private
21226     onKeyUp : function(e){
21227         if(!e.isNavKeyPress()){
21228             this.autoSize();
21229         }
21230     },
21231
21232     /**
21233      * Resets the current field value to the originally-loaded value and clears any validation messages.
21234      * Also adds emptyText and emptyClass if the original value was blank.
21235      */
21236     reset : function(){
21237         Roo.form.TextField.superclass.reset.call(this);
21238         this.applyEmptyText();
21239     },
21240
21241     applyEmptyText : function(){
21242         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21243             this.setRawValue(this.emptyText);
21244             this.el.addClass(this.emptyClass);
21245         }
21246     },
21247
21248     // private
21249     preFocus : function(){
21250         if(this.emptyText){
21251             if(this.el.dom.value == this.emptyText){
21252                 this.setRawValue('');
21253             }
21254             this.el.removeClass(this.emptyClass);
21255         }
21256         if(this.selectOnFocus){
21257             this.el.dom.select();
21258         }
21259     },
21260
21261     // private
21262     postBlur : function(){
21263         this.applyEmptyText();
21264     },
21265
21266     // private
21267     filterKeys : function(e){
21268         var k = e.getKey();
21269         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21270             return;
21271         }
21272         var c = e.getCharCode(), cc = String.fromCharCode(c);
21273         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21274             return;
21275         }
21276         if(!this.maskRe.test(cc)){
21277             e.stopEvent();
21278         }
21279     },
21280
21281     setValue : function(v){
21282         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21283             this.el.removeClass(this.emptyClass);
21284         }
21285         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21286         this.applyEmptyText();
21287         this.autoSize();
21288     },
21289
21290     /**
21291      * Validates a value according to the field's validation rules and marks the field as invalid
21292      * if the validation fails
21293      * @param {Mixed} value The value to validate
21294      * @return {Boolean} True if the value is valid, else false
21295      */
21296     validateValue : function(value){
21297         if(value.length < 1 || value === this.emptyText){ // if it's blank
21298              if(this.allowBlank){
21299                 this.clearInvalid();
21300                 return true;
21301              }else{
21302                 this.markInvalid(this.blankText);
21303                 return false;
21304              }
21305         }
21306         if(value.length < this.minLength){
21307             this.markInvalid(String.format(this.minLengthText, this.minLength));
21308             return false;
21309         }
21310         if(value.length > this.maxLength){
21311             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21312             return false;
21313         }
21314         if(this.vtype){
21315             var vt = Roo.form.VTypes;
21316             if(!vt[this.vtype](value, this)){
21317                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21318                 return false;
21319             }
21320         }
21321         if(typeof this.validator == "function"){
21322             var msg = this.validator(value);
21323             if(msg !== true){
21324                 this.markInvalid(msg);
21325                 return false;
21326             }
21327         }
21328         if(this.regex && !this.regex.test(value)){
21329             this.markInvalid(this.regexText);
21330             return false;
21331         }
21332         return true;
21333     },
21334
21335     /**
21336      * Selects text in this field
21337      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21338      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21339      */
21340     selectText : function(start, end){
21341         var v = this.getRawValue();
21342         if(v.length > 0){
21343             start = start === undefined ? 0 : start;
21344             end = end === undefined ? v.length : end;
21345             var d = this.el.dom;
21346             if(d.setSelectionRange){
21347                 d.setSelectionRange(start, end);
21348             }else if(d.createTextRange){
21349                 var range = d.createTextRange();
21350                 range.moveStart("character", start);
21351                 range.moveEnd("character", v.length-end);
21352                 range.select();
21353             }
21354         }
21355     },
21356
21357     /**
21358      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21359      * This only takes effect if grow = true, and fires the autosize event.
21360      */
21361     autoSize : function(){
21362         if(!this.grow || !this.rendered){
21363             return;
21364         }
21365         if(!this.metrics){
21366             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21367         }
21368         var el = this.el;
21369         var v = el.dom.value;
21370         var d = document.createElement('div');
21371         d.appendChild(document.createTextNode(v));
21372         v = d.innerHTML;
21373         d = null;
21374         v += "&#160;";
21375         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21376         this.el.setWidth(w);
21377         this.fireEvent("autosize", this, w);
21378     }
21379 });/*
21380  * Based on:
21381  * Ext JS Library 1.1.1
21382  * Copyright(c) 2006-2007, Ext JS, LLC.
21383  *
21384  * Originally Released Under LGPL - original licence link has changed is not relivant.
21385  *
21386  * Fork - LGPL
21387  * <script type="text/javascript">
21388  */
21389  
21390 /**
21391  * @class Roo.form.Hidden
21392  * @extends Roo.form.TextField
21393  * Simple Hidden element used on forms 
21394  * 
21395  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21396  * 
21397  * @constructor
21398  * Creates a new Hidden form element.
21399  * @param {Object} config Configuration options
21400  */
21401
21402
21403
21404 // easy hidden field...
21405 Roo.form.Hidden = function(config){
21406     Roo.form.Hidden.superclass.constructor.call(this, config);
21407 };
21408   
21409 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21410     fieldLabel:      '',
21411     inputType:      'hidden',
21412     width:          50,
21413     allowBlank:     true,
21414     labelSeparator: '',
21415     hidden:         true,
21416     itemCls :       'x-form-item-display-none'
21417
21418
21419 });
21420
21421
21422 /*
21423  * Based on:
21424  * Ext JS Library 1.1.1
21425  * Copyright(c) 2006-2007, Ext JS, LLC.
21426  *
21427  * Originally Released Under LGPL - original licence link has changed is not relivant.
21428  *
21429  * Fork - LGPL
21430  * <script type="text/javascript">
21431  */
21432  
21433 /**
21434  * @class Roo.form.TriggerField
21435  * @extends Roo.form.TextField
21436  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21437  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21438  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21439  * for which you can provide a custom implementation.  For example:
21440  * <pre><code>
21441 var trigger = new Roo.form.TriggerField();
21442 trigger.onTriggerClick = myTriggerFn;
21443 trigger.applyTo('my-field');
21444 </code></pre>
21445  *
21446  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21447  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21448  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21449  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21450  * @constructor
21451  * Create a new TriggerField.
21452  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21453  * to the base TextField)
21454  */
21455 Roo.form.TriggerField = function(config){
21456     this.mimicing = false;
21457     Roo.form.TriggerField.superclass.constructor.call(this, config);
21458 };
21459
21460 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21461     /**
21462      * @cfg {String} triggerClass A CSS class to apply to the trigger
21463      */
21464     /**
21465      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21466      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21467      */
21468     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21469     /**
21470      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21471      */
21472     hideTrigger:false,
21473
21474     /** @cfg {Boolean} grow @hide */
21475     /** @cfg {Number} growMin @hide */
21476     /** @cfg {Number} growMax @hide */
21477
21478     /**
21479      * @hide 
21480      * @method
21481      */
21482     autoSize: Roo.emptyFn,
21483     // private
21484     monitorTab : true,
21485     // private
21486     deferHeight : true,
21487
21488     
21489     actionMode : 'wrap',
21490     // private
21491     onResize : function(w, h){
21492         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21493         if(typeof w == 'number'){
21494             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
21495         }
21496     },
21497
21498     // private
21499     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21500
21501     // private
21502     getResizeEl : function(){
21503         return this.wrap;
21504     },
21505
21506     // private
21507     getPositionEl : function(){
21508         return this.wrap;
21509     },
21510
21511     // private
21512     alignErrorIcon : function(){
21513         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21514     },
21515
21516     // private
21517     onRender : function(ct, position){
21518         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21519         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21520         this.trigger = this.wrap.createChild(this.triggerConfig ||
21521                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21522         if(this.hideTrigger){
21523             this.trigger.setDisplayed(false);
21524         }
21525         this.initTrigger();
21526         if(!this.width){
21527             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21528         }
21529     },
21530
21531     // private
21532     initTrigger : function(){
21533         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21534         this.trigger.addClassOnOver('x-form-trigger-over');
21535         this.trigger.addClassOnClick('x-form-trigger-click');
21536     },
21537
21538     // private
21539     onDestroy : function(){
21540         if(this.trigger){
21541             this.trigger.removeAllListeners();
21542             this.trigger.remove();
21543         }
21544         if(this.wrap){
21545             this.wrap.remove();
21546         }
21547         Roo.form.TriggerField.superclass.onDestroy.call(this);
21548     },
21549
21550     // private
21551     onFocus : function(){
21552         Roo.form.TriggerField.superclass.onFocus.call(this);
21553         if(!this.mimicing){
21554             this.wrap.addClass('x-trigger-wrap-focus');
21555             this.mimicing = true;
21556             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21557             if(this.monitorTab){
21558                 this.el.on("keydown", this.checkTab, this);
21559             }
21560         }
21561     },
21562
21563     // private
21564     checkTab : function(e){
21565         if(e.getKey() == e.TAB){
21566             this.triggerBlur();
21567         }
21568     },
21569
21570     // private
21571     onBlur : function(){
21572         // do nothing
21573     },
21574
21575     // private
21576     mimicBlur : function(e, t){
21577         if(!this.wrap.contains(t) && this.validateBlur()){
21578             this.triggerBlur();
21579         }
21580     },
21581
21582     // private
21583     triggerBlur : function(){
21584         this.mimicing = false;
21585         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21586         if(this.monitorTab){
21587             this.el.un("keydown", this.checkTab, this);
21588         }
21589         this.wrap.removeClass('x-trigger-wrap-focus');
21590         Roo.form.TriggerField.superclass.onBlur.call(this);
21591     },
21592
21593     // private
21594     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21595     validateBlur : function(e, t){
21596         return true;
21597     },
21598
21599     // private
21600     onDisable : function(){
21601         Roo.form.TriggerField.superclass.onDisable.call(this);
21602         if(this.wrap){
21603             this.wrap.addClass('x-item-disabled');
21604         }
21605     },
21606
21607     // private
21608     onEnable : function(){
21609         Roo.form.TriggerField.superclass.onEnable.call(this);
21610         if(this.wrap){
21611             this.wrap.removeClass('x-item-disabled');
21612         }
21613     },
21614
21615     // private
21616     onShow : function(){
21617         var ae = this.getActionEl();
21618         
21619         if(ae){
21620             ae.dom.style.display = '';
21621             ae.dom.style.visibility = 'visible';
21622         }
21623     },
21624
21625     // private
21626     
21627     onHide : function(){
21628         var ae = this.getActionEl();
21629         ae.dom.style.display = 'none';
21630     },
21631
21632     /**
21633      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21634      * by an implementing function.
21635      * @method
21636      * @param {EventObject} e
21637      */
21638     onTriggerClick : Roo.emptyFn
21639 });
21640
21641 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21642 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21643 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21644 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21645     initComponent : function(){
21646         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21647
21648         this.triggerConfig = {
21649             tag:'span', cls:'x-form-twin-triggers', cn:[
21650             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21651             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21652         ]};
21653     },
21654
21655     getTrigger : function(index){
21656         return this.triggers[index];
21657     },
21658
21659     initTrigger : function(){
21660         var ts = this.trigger.select('.x-form-trigger', true);
21661         this.wrap.setStyle('overflow', 'hidden');
21662         var triggerField = this;
21663         ts.each(function(t, all, index){
21664             t.hide = function(){
21665                 var w = triggerField.wrap.getWidth();
21666                 this.dom.style.display = 'none';
21667                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21668             };
21669             t.show = function(){
21670                 var w = triggerField.wrap.getWidth();
21671                 this.dom.style.display = '';
21672                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21673             };
21674             var triggerIndex = 'Trigger'+(index+1);
21675
21676             if(this['hide'+triggerIndex]){
21677                 t.dom.style.display = 'none';
21678             }
21679             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21680             t.addClassOnOver('x-form-trigger-over');
21681             t.addClassOnClick('x-form-trigger-click');
21682         }, this);
21683         this.triggers = ts.elements;
21684     },
21685
21686     onTrigger1Click : Roo.emptyFn,
21687     onTrigger2Click : Roo.emptyFn
21688 });/*
21689  * Based on:
21690  * Ext JS Library 1.1.1
21691  * Copyright(c) 2006-2007, Ext JS, LLC.
21692  *
21693  * Originally Released Under LGPL - original licence link has changed is not relivant.
21694  *
21695  * Fork - LGPL
21696  * <script type="text/javascript">
21697  */
21698  
21699 /**
21700  * @class Roo.form.TextArea
21701  * @extends Roo.form.TextField
21702  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21703  * support for auto-sizing.
21704  * @constructor
21705  * Creates a new TextArea
21706  * @param {Object} config Configuration options
21707  */
21708 Roo.form.TextArea = function(config){
21709     Roo.form.TextArea.superclass.constructor.call(this, config);
21710     // these are provided exchanges for backwards compat
21711     // minHeight/maxHeight were replaced by growMin/growMax to be
21712     // compatible with TextField growing config values
21713     if(this.minHeight !== undefined){
21714         this.growMin = this.minHeight;
21715     }
21716     if(this.maxHeight !== undefined){
21717         this.growMax = this.maxHeight;
21718     }
21719 };
21720
21721 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21722     /**
21723      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21724      */
21725     growMin : 60,
21726     /**
21727      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21728      */
21729     growMax: 1000,
21730     /**
21731      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21732      * in the field (equivalent to setting overflow: hidden, defaults to false)
21733      */
21734     preventScrollbars: false,
21735     /**
21736      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21737      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21738      */
21739
21740     // private
21741     onRender : function(ct, position){
21742         if(!this.el){
21743             this.defaultAutoCreate = {
21744                 tag: "textarea",
21745                 style:"width:300px;height:60px;",
21746                 autocomplete: "off"
21747             };
21748         }
21749         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21750         if(this.grow){
21751             this.textSizeEl = Roo.DomHelper.append(document.body, {
21752                 tag: "pre", cls: "x-form-grow-sizer"
21753             });
21754             if(this.preventScrollbars){
21755                 this.el.setStyle("overflow", "hidden");
21756             }
21757             this.el.setHeight(this.growMin);
21758         }
21759     },
21760
21761     onDestroy : function(){
21762         if(this.textSizeEl){
21763             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21764         }
21765         Roo.form.TextArea.superclass.onDestroy.call(this);
21766     },
21767
21768     // private
21769     onKeyUp : function(e){
21770         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21771             this.autoSize();
21772         }
21773     },
21774
21775     /**
21776      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21777      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21778      */
21779     autoSize : function(){
21780         if(!this.grow || !this.textSizeEl){
21781             return;
21782         }
21783         var el = this.el;
21784         var v = el.dom.value;
21785         var ts = this.textSizeEl;
21786
21787         ts.innerHTML = '';
21788         ts.appendChild(document.createTextNode(v));
21789         v = ts.innerHTML;
21790
21791         Roo.fly(ts).setWidth(this.el.getWidth());
21792         if(v.length < 1){
21793             v = "&#160;&#160;";
21794         }else{
21795             if(Roo.isIE){
21796                 v = v.replace(/\n/g, '<p>&#160;</p>');
21797             }
21798             v += "&#160;\n&#160;";
21799         }
21800         ts.innerHTML = v;
21801         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21802         if(h != this.lastHeight){
21803             this.lastHeight = h;
21804             this.el.setHeight(h);
21805             this.fireEvent("autosize", this, h);
21806         }
21807     }
21808 });/*
21809  * Based on:
21810  * Ext JS Library 1.1.1
21811  * Copyright(c) 2006-2007, Ext JS, LLC.
21812  *
21813  * Originally Released Under LGPL - original licence link has changed is not relivant.
21814  *
21815  * Fork - LGPL
21816  * <script type="text/javascript">
21817  */
21818  
21819
21820 /**
21821  * @class Roo.form.NumberField
21822  * @extends Roo.form.TextField
21823  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21824  * @constructor
21825  * Creates a new NumberField
21826  * @param {Object} config Configuration options
21827  */
21828 Roo.form.NumberField = function(config){
21829     Roo.form.NumberField.superclass.constructor.call(this, config);
21830 };
21831
21832 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21833     /**
21834      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21835      */
21836     fieldClass: "x-form-field x-form-num-field",
21837     /**
21838      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21839      */
21840     allowDecimals : true,
21841     /**
21842      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21843      */
21844     decimalSeparator : ".",
21845     /**
21846      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21847      */
21848     decimalPrecision : 2,
21849     /**
21850      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21851      */
21852     allowNegative : true,
21853     /**
21854      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21855      */
21856     minValue : Number.NEGATIVE_INFINITY,
21857     /**
21858      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21859      */
21860     maxValue : Number.MAX_VALUE,
21861     /**
21862      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21863      */
21864     minText : "The minimum value for this field is {0}",
21865     /**
21866      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21867      */
21868     maxText : "The maximum value for this field is {0}",
21869     /**
21870      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21871      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21872      */
21873     nanText : "{0} is not a valid number",
21874
21875     // private
21876     initEvents : function(){
21877         Roo.form.NumberField.superclass.initEvents.call(this);
21878         var allowed = "0123456789";
21879         if(this.allowDecimals){
21880             allowed += this.decimalSeparator;
21881         }
21882         if(this.allowNegative){
21883             allowed += "-";
21884         }
21885         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21886         var keyPress = function(e){
21887             var k = e.getKey();
21888             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21889                 return;
21890             }
21891             var c = e.getCharCode();
21892             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21893                 e.stopEvent();
21894             }
21895         };
21896         this.el.on("keypress", keyPress, this);
21897     },
21898
21899     // private
21900     validateValue : function(value){
21901         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21902             return false;
21903         }
21904         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21905              return true;
21906         }
21907         var num = this.parseValue(value);
21908         if(isNaN(num)){
21909             this.markInvalid(String.format(this.nanText, value));
21910             return false;
21911         }
21912         if(num < this.minValue){
21913             this.markInvalid(String.format(this.minText, this.minValue));
21914             return false;
21915         }
21916         if(num > this.maxValue){
21917             this.markInvalid(String.format(this.maxText, this.maxValue));
21918             return false;
21919         }
21920         return true;
21921     },
21922
21923     getValue : function(){
21924         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21925     },
21926
21927     // private
21928     parseValue : function(value){
21929         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21930         return isNaN(value) ? '' : value;
21931     },
21932
21933     // private
21934     fixPrecision : function(value){
21935         var nan = isNaN(value);
21936         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
21937             return nan ? '' : value;
21938         }
21939         return parseFloat(value).toFixed(this.decimalPrecision);
21940     },
21941
21942     setValue : function(v){
21943         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
21944     },
21945
21946     // private
21947     decimalPrecisionFcn : function(v){
21948         return Math.floor(v);
21949     },
21950
21951     beforeBlur : function(){
21952         var v = this.parseValue(this.getRawValue());
21953         if(v){
21954             this.setValue(this.fixPrecision(v));
21955         }
21956     }
21957 });/*
21958  * Based on:
21959  * Ext JS Library 1.1.1
21960  * Copyright(c) 2006-2007, Ext JS, LLC.
21961  *
21962  * Originally Released Under LGPL - original licence link has changed is not relivant.
21963  *
21964  * Fork - LGPL
21965  * <script type="text/javascript">
21966  */
21967  
21968 /**
21969  * @class Roo.form.DateField
21970  * @extends Roo.form.TriggerField
21971  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
21972 * @constructor
21973 * Create a new DateField
21974 * @param {Object} config
21975  */
21976 Roo.form.DateField = function(config){
21977     Roo.form.DateField.superclass.constructor.call(this, config);
21978     
21979       this.addEvents({
21980          
21981         /**
21982          * @event select
21983          * Fires when a date is selected
21984              * @param {Roo.form.DateField} combo This combo box
21985              * @param {Date} date The date selected
21986              */
21987         'select' : true
21988          
21989     });
21990     
21991     
21992     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
21993     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
21994     this.ddMatch = null;
21995     if(this.disabledDates){
21996         var dd = this.disabledDates;
21997         var re = "(?:";
21998         for(var i = 0; i < dd.length; i++){
21999             re += dd[i];
22000             if(i != dd.length-1) re += "|";
22001         }
22002         this.ddMatch = new RegExp(re + ")");
22003     }
22004 };
22005
22006 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22007     /**
22008      * @cfg {String} format
22009      * The default date format string which can be overriden for localization support.  The format must be
22010      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22011      */
22012     format : "m/d/y",
22013     /**
22014      * @cfg {String} altFormats
22015      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22016      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22017      */
22018     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22019     /**
22020      * @cfg {Array} disabledDays
22021      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22022      */
22023     disabledDays : null,
22024     /**
22025      * @cfg {String} disabledDaysText
22026      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22027      */
22028     disabledDaysText : "Disabled",
22029     /**
22030      * @cfg {Array} disabledDates
22031      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22032      * expression so they are very powerful. Some examples:
22033      * <ul>
22034      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22035      * <li>["03/08", "09/16"] would disable those days for every year</li>
22036      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22037      * <li>["03/../2006"] would disable every day in March 2006</li>
22038      * <li>["^03"] would disable every day in every March</li>
22039      * </ul>
22040      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22041      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22042      */
22043     disabledDates : null,
22044     /**
22045      * @cfg {String} disabledDatesText
22046      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22047      */
22048     disabledDatesText : "Disabled",
22049     /**
22050      * @cfg {Date/String} minValue
22051      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22052      * valid format (defaults to null).
22053      */
22054     minValue : null,
22055     /**
22056      * @cfg {Date/String} maxValue
22057      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22058      * valid format (defaults to null).
22059      */
22060     maxValue : null,
22061     /**
22062      * @cfg {String} minText
22063      * The error text to display when the date in the cell is before minValue (defaults to
22064      * 'The date in this field must be after {minValue}').
22065      */
22066     minText : "The date in this field must be equal to or after {0}",
22067     /**
22068      * @cfg {String} maxText
22069      * The error text to display when the date in the cell is after maxValue (defaults to
22070      * 'The date in this field must be before {maxValue}').
22071      */
22072     maxText : "The date in this field must be equal to or before {0}",
22073     /**
22074      * @cfg {String} invalidText
22075      * The error text to display when the date in the field is invalid (defaults to
22076      * '{value} is not a valid date - it must be in the format {format}').
22077      */
22078     invalidText : "{0} is not a valid date - it must be in the format {1}",
22079     /**
22080      * @cfg {String} triggerClass
22081      * An additional CSS class used to style the trigger button.  The trigger will always get the
22082      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22083      * which displays a calendar icon).
22084      */
22085     triggerClass : 'x-form-date-trigger',
22086     
22087
22088     /**
22089      * @cfg {bool} useIso
22090      * if enabled, then the date field will use a hidden field to store the 
22091      * real value as iso formated date. default (false)
22092      */ 
22093     useIso : false,
22094     /**
22095      * @cfg {String/Object} autoCreate
22096      * A DomHelper element spec, or true for a default element spec (defaults to
22097      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22098      */ 
22099     // private
22100     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22101     
22102     // private
22103     hiddenField: false,
22104     
22105     onRender : function(ct, position)
22106     {
22107         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22108         if (this.useIso) {
22109             this.el.dom.removeAttribute('name'); 
22110             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22111                     'before', true);
22112             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22113             // prevent input submission
22114             this.hiddenName = this.name;
22115         }
22116             
22117             
22118     },
22119     
22120     // private
22121     validateValue : function(value)
22122     {
22123         value = this.formatDate(value);
22124         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22125             return false;
22126         }
22127         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22128              return true;
22129         }
22130         var svalue = value;
22131         value = this.parseDate(value);
22132         if(!value){
22133             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22134             return false;
22135         }
22136         var time = value.getTime();
22137         if(this.minValue && time < this.minValue.getTime()){
22138             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22139             return false;
22140         }
22141         if(this.maxValue && time > this.maxValue.getTime()){
22142             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22143             return false;
22144         }
22145         if(this.disabledDays){
22146             var day = value.getDay();
22147             for(var i = 0; i < this.disabledDays.length; i++) {
22148                 if(day === this.disabledDays[i]){
22149                     this.markInvalid(this.disabledDaysText);
22150                     return false;
22151                 }
22152             }
22153         }
22154         var fvalue = this.formatDate(value);
22155         if(this.ddMatch && this.ddMatch.test(fvalue)){
22156             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22157             return false;
22158         }
22159         return true;
22160     },
22161
22162     // private
22163     // Provides logic to override the default TriggerField.validateBlur which just returns true
22164     validateBlur : function(){
22165         return !this.menu || !this.menu.isVisible();
22166     },
22167
22168     /**
22169      * Returns the current date value of the date field.
22170      * @return {Date} The date value
22171      */
22172     getValue : function(){
22173         
22174         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22175     },
22176
22177     /**
22178      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22179      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22180      * (the default format used is "m/d/y").
22181      * <br />Usage:
22182      * <pre><code>
22183 //All of these calls set the same date value (May 4, 2006)
22184
22185 //Pass a date object:
22186 var dt = new Date('5/4/06');
22187 dateField.setValue(dt);
22188
22189 //Pass a date string (default format):
22190 dateField.setValue('5/4/06');
22191
22192 //Pass a date string (custom format):
22193 dateField.format = 'Y-m-d';
22194 dateField.setValue('2006-5-4');
22195 </code></pre>
22196      * @param {String/Date} date The date or valid date string
22197      */
22198     setValue : function(date){
22199         if (this.hiddenField) {
22200             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22201         }
22202         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22203     },
22204
22205     // private
22206     parseDate : function(value){
22207         if(!value || value instanceof Date){
22208             return value;
22209         }
22210         var v = Date.parseDate(value, this.format);
22211         if(!v && this.altFormats){
22212             if(!this.altFormatsArray){
22213                 this.altFormatsArray = this.altFormats.split("|");
22214             }
22215             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22216                 v = Date.parseDate(value, this.altFormatsArray[i]);
22217             }
22218         }
22219         return v;
22220     },
22221
22222     // private
22223     formatDate : function(date, fmt){
22224         return (!date || !(date instanceof Date)) ?
22225                date : date.dateFormat(fmt || this.format);
22226     },
22227
22228     // private
22229     menuListeners : {
22230         select: function(m, d){
22231             this.setValue(d);
22232             this.fireEvent('select', this, d);
22233         },
22234         show : function(){ // retain focus styling
22235             this.onFocus();
22236         },
22237         hide : function(){
22238             this.focus.defer(10, this);
22239             var ml = this.menuListeners;
22240             this.menu.un("select", ml.select,  this);
22241             this.menu.un("show", ml.show,  this);
22242             this.menu.un("hide", ml.hide,  this);
22243         }
22244     },
22245
22246     // private
22247     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22248     onTriggerClick : function(){
22249         if(this.disabled){
22250             return;
22251         }
22252         if(this.menu == null){
22253             this.menu = new Roo.menu.DateMenu();
22254         }
22255         Roo.apply(this.menu.picker,  {
22256             showClear: this.allowBlank,
22257             minDate : this.minValue,
22258             maxDate : this.maxValue,
22259             disabledDatesRE : this.ddMatch,
22260             disabledDatesText : this.disabledDatesText,
22261             disabledDays : this.disabledDays,
22262             disabledDaysText : this.disabledDaysText,
22263             format : this.format,
22264             minText : String.format(this.minText, this.formatDate(this.minValue)),
22265             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22266         });
22267         this.menu.on(Roo.apply({}, this.menuListeners, {
22268             scope:this
22269         }));
22270         this.menu.picker.setValue(this.getValue() || new Date());
22271         this.menu.show(this.el, "tl-bl?");
22272     },
22273
22274     beforeBlur : function(){
22275         var v = this.parseDate(this.getRawValue());
22276         if(v){
22277             this.setValue(v);
22278         }
22279     }
22280
22281     /** @cfg {Boolean} grow @hide */
22282     /** @cfg {Number} growMin @hide */
22283     /** @cfg {Number} growMax @hide */
22284     /**
22285      * @hide
22286      * @method autoSize
22287      */
22288 });/*
22289  * Based on:
22290  * Ext JS Library 1.1.1
22291  * Copyright(c) 2006-2007, Ext JS, LLC.
22292  *
22293  * Originally Released Under LGPL - original licence link has changed is not relivant.
22294  *
22295  * Fork - LGPL
22296  * <script type="text/javascript">
22297  */
22298  
22299
22300 /**
22301  * @class Roo.form.ComboBox
22302  * @extends Roo.form.TriggerField
22303  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22304  * @constructor
22305  * Create a new ComboBox.
22306  * @param {Object} config Configuration options
22307  */
22308 Roo.form.ComboBox = function(config){
22309     Roo.form.ComboBox.superclass.constructor.call(this, config);
22310     this.addEvents({
22311         /**
22312          * @event expand
22313          * Fires when the dropdown list is expanded
22314              * @param {Roo.form.ComboBox} combo This combo box
22315              */
22316         'expand' : true,
22317         /**
22318          * @event collapse
22319          * Fires when the dropdown list is collapsed
22320              * @param {Roo.form.ComboBox} combo This combo box
22321              */
22322         'collapse' : true,
22323         /**
22324          * @event beforeselect
22325          * Fires before a list item is selected. Return false to cancel the selection.
22326              * @param {Roo.form.ComboBox} combo This combo box
22327              * @param {Roo.data.Record} record The data record returned from the underlying store
22328              * @param {Number} index The index of the selected item in the dropdown list
22329              */
22330         'beforeselect' : true,
22331         /**
22332          * @event select
22333          * Fires when a list item is selected
22334              * @param {Roo.form.ComboBox} combo This combo box
22335              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22336              * @param {Number} index The index of the selected item in the dropdown list
22337              */
22338         'select' : true,
22339         /**
22340          * @event beforequery
22341          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22342          * The event object passed has these properties:
22343              * @param {Roo.form.ComboBox} combo This combo box
22344              * @param {String} query The query
22345              * @param {Boolean} forceAll true to force "all" query
22346              * @param {Boolean} cancel true to cancel the query
22347              * @param {Object} e The query event object
22348              */
22349         'beforequery': true
22350     });
22351     if(this.transform){
22352         this.allowDomMove = false;
22353         var s = Roo.getDom(this.transform);
22354         if(!this.hiddenName){
22355             this.hiddenName = s.name;
22356         }
22357         if(!this.store){
22358             this.mode = 'local';
22359             var d = [], opts = s.options;
22360             for(var i = 0, len = opts.length;i < len; i++){
22361                 var o = opts[i];
22362                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22363                 if(o.selected) {
22364                     this.value = value;
22365                 }
22366                 d.push([value, o.text]);
22367             }
22368             this.store = new Roo.data.SimpleStore({
22369                 'id': 0,
22370                 fields: ['value', 'text'],
22371                 data : d
22372             });
22373             this.valueField = 'value';
22374             this.displayField = 'text';
22375         }
22376         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22377         if(!this.lazyRender){
22378             this.target = true;
22379             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22380             s.parentNode.removeChild(s); // remove it
22381             this.render(this.el.parentNode);
22382         }else{
22383             s.parentNode.removeChild(s); // remove it
22384         }
22385
22386     }
22387     if (this.store) {
22388         this.store = Roo.factory(this.store, Roo.data);
22389     }
22390     
22391     this.selectedIndex = -1;
22392     if(this.mode == 'local'){
22393         if(config.queryDelay === undefined){
22394             this.queryDelay = 10;
22395         }
22396         if(config.minChars === undefined){
22397             this.minChars = 0;
22398         }
22399     }
22400 };
22401
22402 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22403     /**
22404      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22405      */
22406     /**
22407      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22408      * rendering into an Roo.Editor, defaults to false)
22409      */
22410     /**
22411      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22412      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22413      */
22414     /**
22415      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22416      */
22417     /**
22418      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22419      * the dropdown list (defaults to undefined, with no header element)
22420      */
22421
22422      /**
22423      * @cfg {String/Roo.Template} tpl The template to use to render the output
22424      */
22425      
22426     // private
22427     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22428     /**
22429      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22430      */
22431     listWidth: undefined,
22432     /**
22433      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22434      * mode = 'remote' or 'text' if mode = 'local')
22435      */
22436     displayField: undefined,
22437     /**
22438      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22439      * mode = 'remote' or 'value' if mode = 'local'). 
22440      * Note: use of a valueField requires the user make a selection
22441      * in order for a value to be mapped.
22442      */
22443     valueField: undefined,
22444     /**
22445      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22446      * field's data value (defaults to the underlying DOM element's name)
22447      */
22448     hiddenName: undefined,
22449     /**
22450      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22451      */
22452     listClass: '',
22453     /**
22454      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22455      */
22456     selectedClass: 'x-combo-selected',
22457     /**
22458      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22459      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22460      * which displays a downward arrow icon).
22461      */
22462     triggerClass : 'x-form-arrow-trigger',
22463     /**
22464      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22465      */
22466     shadow:'sides',
22467     /**
22468      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22469      * anchor positions (defaults to 'tl-bl')
22470      */
22471     listAlign: 'tl-bl?',
22472     /**
22473      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22474      */
22475     maxHeight: 300,
22476     /**
22477      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22478      * query specified by the allQuery config option (defaults to 'query')
22479      */
22480     triggerAction: 'query',
22481     /**
22482      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22483      * (defaults to 4, does not apply if editable = false)
22484      */
22485     minChars : 4,
22486     /**
22487      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22488      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22489      */
22490     typeAhead: false,
22491     /**
22492      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22493      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22494      */
22495     queryDelay: 500,
22496     /**
22497      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22498      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22499      */
22500     pageSize: 0,
22501     /**
22502      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22503      * when editable = true (defaults to false)
22504      */
22505     selectOnFocus:false,
22506     /**
22507      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22508      */
22509     queryParam: 'query',
22510     /**
22511      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22512      * when mode = 'remote' (defaults to 'Loading...')
22513      */
22514     loadingText: 'Loading...',
22515     /**
22516      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22517      */
22518     resizable: false,
22519     /**
22520      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22521      */
22522     handleHeight : 8,
22523     /**
22524      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22525      * traditional select (defaults to true)
22526      */
22527     editable: true,
22528     /**
22529      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22530      */
22531     allQuery: '',
22532     /**
22533      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22534      */
22535     mode: 'remote',
22536     /**
22537      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22538      * listWidth has a higher value)
22539      */
22540     minListWidth : 70,
22541     /**
22542      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22543      * allow the user to set arbitrary text into the field (defaults to false)
22544      */
22545     forceSelection:false,
22546     /**
22547      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22548      * if typeAhead = true (defaults to 250)
22549      */
22550     typeAheadDelay : 250,
22551     /**
22552      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22553      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22554      */
22555     valueNotFoundText : undefined,
22556     /**
22557      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22558      */
22559     blockFocus : false,
22560     
22561     /**
22562      * @cfg {bool} disableClear Disable showing of clear button.
22563      */
22564     disableClear : false,
22565     
22566     // private
22567     onRender : function(ct, position){
22568         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22569         if(this.hiddenName){
22570             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22571                     'before', true);
22572             this.hiddenField.value =
22573                 this.hiddenValue !== undefined ? this.hiddenValue :
22574                 this.value !== undefined ? this.value : '';
22575
22576             // prevent input submission
22577             this.el.dom.removeAttribute('name');
22578         }
22579         if(Roo.isGecko){
22580             this.el.dom.setAttribute('autocomplete', 'off');
22581         }
22582
22583         var cls = 'x-combo-list';
22584
22585         this.list = new Roo.Layer({
22586             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22587         });
22588
22589         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22590         this.list.setWidth(lw);
22591         this.list.swallowEvent('mousewheel');
22592         this.assetHeight = 0;
22593
22594         if(this.title){
22595             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22596             this.assetHeight += this.header.getHeight();
22597         }
22598
22599         this.innerList = this.list.createChild({cls:cls+'-inner'});
22600         this.innerList.on('mouseover', this.onViewOver, this);
22601         this.innerList.on('mousemove', this.onViewMove, this);
22602         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22603         
22604         if(this.allowBlank && !this.pageSize && !this.disableClear){
22605             this.footer = this.list.createChild({cls:cls+'-ft'});
22606             this.pageTb = new Roo.Toolbar(this.footer);
22607            
22608         }
22609         if(this.pageSize){
22610             this.footer = this.list.createChild({cls:cls+'-ft'});
22611             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22612                     {pageSize: this.pageSize});
22613             
22614         }
22615         
22616         if (this.pageTb && this.allowBlank && !this.disableClear) {
22617             var _this = this;
22618             this.pageTb.add(new Roo.Toolbar.Fill(), {
22619                 cls: 'x-btn-icon x-btn-clear',
22620                 text: '&#160;',
22621                 handler: function()
22622                 {
22623                     _this.collapse();
22624                     _this.clearValue();
22625                     _this.onSelect(false, -1);
22626                 }
22627             });
22628         }
22629         if (this.footer) {
22630             this.assetHeight += this.footer.getHeight();
22631         }
22632         
22633
22634         if(!this.tpl){
22635             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22636         }
22637
22638         this.view = new Roo.View(this.innerList, this.tpl, {
22639             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22640         });
22641
22642         this.view.on('click', this.onViewClick, this);
22643
22644         this.store.on('beforeload', this.onBeforeLoad, this);
22645         this.store.on('load', this.onLoad, this);
22646         this.store.on('loadexception', this.collapse, this);
22647
22648         if(this.resizable){
22649             this.resizer = new Roo.Resizable(this.list,  {
22650                pinned:true, handles:'se'
22651             });
22652             this.resizer.on('resize', function(r, w, h){
22653                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22654                 this.listWidth = w;
22655                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22656                 this.restrictHeight();
22657             }, this);
22658             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22659         }
22660         if(!this.editable){
22661             this.editable = true;
22662             this.setEditable(false);
22663         }
22664     },
22665
22666     // private
22667     initEvents : function(){
22668         Roo.form.ComboBox.superclass.initEvents.call(this);
22669
22670         this.keyNav = new Roo.KeyNav(this.el, {
22671             "up" : function(e){
22672                 this.inKeyMode = true;
22673                 this.selectPrev();
22674             },
22675
22676             "down" : function(e){
22677                 if(!this.isExpanded()){
22678                     this.onTriggerClick();
22679                 }else{
22680                     this.inKeyMode = true;
22681                     this.selectNext();
22682                 }
22683             },
22684
22685             "enter" : function(e){
22686                 this.onViewClick();
22687                 //return true;
22688             },
22689
22690             "esc" : function(e){
22691                 this.collapse();
22692             },
22693
22694             "tab" : function(e){
22695                 this.onViewClick(false);
22696                 return true;
22697             },
22698
22699             scope : this,
22700
22701             doRelay : function(foo, bar, hname){
22702                 if(hname == 'down' || this.scope.isExpanded()){
22703                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22704                 }
22705                 return true;
22706             },
22707
22708             forceKeyDown: true
22709         });
22710         this.queryDelay = Math.max(this.queryDelay || 10,
22711                 this.mode == 'local' ? 10 : 250);
22712         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
22713         if(this.typeAhead){
22714             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
22715         }
22716         if(this.editable !== false){
22717             this.el.on("keyup", this.onKeyUp, this);
22718         }
22719         if(this.forceSelection){
22720             this.on('blur', this.doForce, this);
22721         }
22722     },
22723
22724     onDestroy : function(){
22725         if(this.view){
22726             this.view.setStore(null);
22727             this.view.el.removeAllListeners();
22728             this.view.el.remove();
22729             this.view.purgeListeners();
22730         }
22731         if(this.list){
22732             this.list.destroy();
22733         }
22734         if(this.store){
22735             this.store.un('beforeload', this.onBeforeLoad, this);
22736             this.store.un('load', this.onLoad, this);
22737             this.store.un('loadexception', this.collapse, this);
22738         }
22739         Roo.form.ComboBox.superclass.onDestroy.call(this);
22740     },
22741
22742     // private
22743     fireKey : function(e){
22744         if(e.isNavKeyPress() && !this.list.isVisible()){
22745             this.fireEvent("specialkey", this, e);
22746         }
22747     },
22748
22749     // private
22750     onResize: function(w, h){
22751         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
22752         if(this.list && this.listWidth === undefined){
22753             var lw = Math.max(w, this.minListWidth);
22754             this.list.setWidth(lw);
22755             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22756         }
22757     },
22758
22759     /**
22760      * Allow or prevent the user from directly editing the field text.  If false is passed,
22761      * the user will only be able to select from the items defined in the dropdown list.  This method
22762      * is the runtime equivalent of setting the 'editable' config option at config time.
22763      * @param {Boolean} value True to allow the user to directly edit the field text
22764      */
22765     setEditable : function(value){
22766         if(value == this.editable){
22767             return;
22768         }
22769         this.editable = value;
22770         if(!value){
22771             this.el.dom.setAttribute('readOnly', true);
22772             this.el.on('mousedown', this.onTriggerClick,  this);
22773             this.el.addClass('x-combo-noedit');
22774         }else{
22775             this.el.dom.setAttribute('readOnly', false);
22776             this.el.un('mousedown', this.onTriggerClick,  this);
22777             this.el.removeClass('x-combo-noedit');
22778         }
22779     },
22780
22781     // private
22782     onBeforeLoad : function(){
22783         if(!this.hasFocus){
22784             return;
22785         }
22786         this.innerList.update(this.loadingText ?
22787                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
22788         this.restrictHeight();
22789         this.selectedIndex = -1;
22790     },
22791
22792     // private
22793     onLoad : function(){
22794         if(!this.hasFocus){
22795             return;
22796         }
22797         if(this.store.getCount() > 0){
22798             this.expand();
22799             this.restrictHeight();
22800             if(this.lastQuery == this.allQuery){
22801                 if(this.editable){
22802                     this.el.dom.select();
22803                 }
22804                 if(!this.selectByValue(this.value, true)){
22805                     this.select(0, true);
22806                 }
22807             }else{
22808                 this.selectNext();
22809                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
22810                     this.taTask.delay(this.typeAheadDelay);
22811                 }
22812             }
22813         }else{
22814             this.onEmptyResults();
22815         }
22816         //this.el.focus();
22817     },
22818
22819     // private
22820     onTypeAhead : function(){
22821         if(this.store.getCount() > 0){
22822             var r = this.store.getAt(0);
22823             var newValue = r.data[this.displayField];
22824             var len = newValue.length;
22825             var selStart = this.getRawValue().length;
22826             if(selStart != len){
22827                 this.setRawValue(newValue);
22828                 this.selectText(selStart, newValue.length);
22829             }
22830         }
22831     },
22832
22833     // private
22834     onSelect : function(record, index){
22835         if(this.fireEvent('beforeselect', this, record, index) !== false){
22836             this.setFromData(index > -1 ? record.data : false);
22837             this.collapse();
22838             this.fireEvent('select', this, record, index);
22839         }
22840     },
22841
22842     /**
22843      * Returns the currently selected field value or empty string if no value is set.
22844      * @return {String} value The selected value
22845      */
22846     getValue : function(){
22847         if(this.valueField){
22848             return typeof this.value != 'undefined' ? this.value : '';
22849         }else{
22850             return Roo.form.ComboBox.superclass.getValue.call(this);
22851         }
22852     },
22853
22854     /**
22855      * Clears any text/value currently set in the field
22856      */
22857     clearValue : function(){
22858         if(this.hiddenField){
22859             this.hiddenField.value = '';
22860         }
22861         this.value = '';
22862         this.setRawValue('');
22863         this.lastSelectionText = '';
22864         this.applyEmptyText();
22865     },
22866
22867     /**
22868      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
22869      * will be displayed in the field.  If the value does not match the data value of an existing item,
22870      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
22871      * Otherwise the field will be blank (although the value will still be set).
22872      * @param {String} value The value to match
22873      */
22874     setValue : function(v){
22875         var text = v;
22876         if(this.valueField){
22877             var r = this.findRecord(this.valueField, v);
22878             if(r){
22879                 text = r.data[this.displayField];
22880             }else if(this.valueNotFoundText !== undefined){
22881                 text = this.valueNotFoundText;
22882             }
22883         }
22884         this.lastSelectionText = text;
22885         if(this.hiddenField){
22886             this.hiddenField.value = v;
22887         }
22888         Roo.form.ComboBox.superclass.setValue.call(this, text);
22889         this.value = v;
22890     },
22891     /**
22892      * @property {Object} the last set data for the element
22893      */
22894     
22895     lastData : false,
22896     /**
22897      * Sets the value of the field based on a object which is related to the record format for the store.
22898      * @param {Object} value the value to set as. or false on reset?
22899      */
22900     setFromData : function(o){
22901         var dv = ''; // display value
22902         var vv = ''; // value value..
22903         this.lastData = o;
22904         if (this.displayField) {
22905             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
22906         } else {
22907             // this is an error condition!!!
22908             console.log('no value field set for '+ this.name);
22909         }
22910         
22911         if(this.valueField){
22912             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
22913         }
22914         if(this.hiddenField){
22915             this.hiddenField.value = vv;
22916             
22917             this.lastSelectionText = dv;
22918             Roo.form.ComboBox.superclass.setValue.call(this, dv);
22919             this.value = vv;
22920             return;
22921         }
22922         // no hidden field.. - we store the value in 'value', but still display
22923         // display field!!!!
22924         this.lastSelectionText = dv;
22925         Roo.form.ComboBox.superclass.setValue.call(this, dv);
22926         this.value = vv;
22927         
22928         
22929     },
22930     // private
22931     reset : function(){
22932         // overridden so that last data is reset..
22933         this.setValue(this.originalValue);
22934         this.clearInvalid();
22935         this.lastData = false;
22936     },
22937     // private
22938     findRecord : function(prop, value){
22939         var record;
22940         if(this.store.getCount() > 0){
22941             this.store.each(function(r){
22942                 if(r.data[prop] == value){
22943                     record = r;
22944                     return false;
22945                 }
22946             });
22947         }
22948         return record;
22949     },
22950
22951     // private
22952     onViewMove : function(e, t){
22953         this.inKeyMode = false;
22954     },
22955
22956     // private
22957     onViewOver : function(e, t){
22958         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
22959             return;
22960         }
22961         var item = this.view.findItemFromChild(t);
22962         if(item){
22963             var index = this.view.indexOf(item);
22964             this.select(index, false);
22965         }
22966     },
22967
22968     // private
22969     onViewClick : function(doFocus){
22970         var index = this.view.getSelectedIndexes()[0];
22971         var r = this.store.getAt(index);
22972         if(r){
22973             this.onSelect(r, index);
22974         }
22975         if(doFocus !== false && !this.blockFocus){
22976             this.el.focus();
22977         }
22978     },
22979
22980     // private
22981     restrictHeight : function(){
22982         this.innerList.dom.style.height = '';
22983         var inner = this.innerList.dom;
22984         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
22985         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
22986         this.list.beginUpdate();
22987         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
22988         this.list.alignTo(this.el, this.listAlign);
22989         this.list.endUpdate();
22990     },
22991
22992     // private
22993     onEmptyResults : function(){
22994         this.collapse();
22995     },
22996
22997     /**
22998      * Returns true if the dropdown list is expanded, else false.
22999      */
23000     isExpanded : function(){
23001         return this.list.isVisible();
23002     },
23003
23004     /**
23005      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23006      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23007      * @param {String} value The data value of the item to select
23008      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23009      * selected item if it is not currently in view (defaults to true)
23010      * @return {Boolean} True if the value matched an item in the list, else false
23011      */
23012     selectByValue : function(v, scrollIntoView){
23013         if(v !== undefined && v !== null){
23014             var r = this.findRecord(this.valueField || this.displayField, v);
23015             if(r){
23016                 this.select(this.store.indexOf(r), scrollIntoView);
23017                 return true;
23018             }
23019         }
23020         return false;
23021     },
23022
23023     /**
23024      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23025      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23026      * @param {Number} index The zero-based index of the list item to select
23027      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23028      * selected item if it is not currently in view (defaults to true)
23029      */
23030     select : function(index, scrollIntoView){
23031         this.selectedIndex = index;
23032         this.view.select(index);
23033         if(scrollIntoView !== false){
23034             var el = this.view.getNode(index);
23035             if(el){
23036                 this.innerList.scrollChildIntoView(el, false);
23037             }
23038         }
23039     },
23040
23041     // private
23042     selectNext : function(){
23043         var ct = this.store.getCount();
23044         if(ct > 0){
23045             if(this.selectedIndex == -1){
23046                 this.select(0);
23047             }else if(this.selectedIndex < ct-1){
23048                 this.select(this.selectedIndex+1);
23049             }
23050         }
23051     },
23052
23053     // private
23054     selectPrev : function(){
23055         var ct = this.store.getCount();
23056         if(ct > 0){
23057             if(this.selectedIndex == -1){
23058                 this.select(0);
23059             }else if(this.selectedIndex != 0){
23060                 this.select(this.selectedIndex-1);
23061             }
23062         }
23063     },
23064
23065     // private
23066     onKeyUp : function(e){
23067         if(this.editable !== false && !e.isSpecialKey()){
23068             this.lastKey = e.getKey();
23069             this.dqTask.delay(this.queryDelay);
23070         }
23071     },
23072
23073     // private
23074     validateBlur : function(){
23075         return !this.list || !this.list.isVisible();   
23076     },
23077
23078     // private
23079     initQuery : function(){
23080         this.doQuery(this.getRawValue());
23081     },
23082
23083     // private
23084     doForce : function(){
23085         if(this.el.dom.value.length > 0){
23086             this.el.dom.value =
23087                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23088             this.applyEmptyText();
23089         }
23090     },
23091
23092     /**
23093      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23094      * query allowing the query action to be canceled if needed.
23095      * @param {String} query The SQL query to execute
23096      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23097      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23098      * saved in the current store (defaults to false)
23099      */
23100     doQuery : function(q, forceAll){
23101         if(q === undefined || q === null){
23102             q = '';
23103         }
23104         var qe = {
23105             query: q,
23106             forceAll: forceAll,
23107             combo: this,
23108             cancel:false
23109         };
23110         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23111             return false;
23112         }
23113         q = qe.query;
23114         forceAll = qe.forceAll;
23115         if(forceAll === true || (q.length >= this.minChars)){
23116             if(this.lastQuery != q){
23117                 this.lastQuery = q;
23118                 if(this.mode == 'local'){
23119                     this.selectedIndex = -1;
23120                     if(forceAll){
23121                         this.store.clearFilter();
23122                     }else{
23123                         this.store.filter(this.displayField, q);
23124                     }
23125                     this.onLoad();
23126                 }else{
23127                     this.store.baseParams[this.queryParam] = q;
23128                     this.store.load({
23129                         params: this.getParams(q)
23130                     });
23131                     this.expand();
23132                 }
23133             }else{
23134                 this.selectedIndex = -1;
23135                 this.onLoad();   
23136             }
23137         }
23138     },
23139
23140     // private
23141     getParams : function(q){
23142         var p = {};
23143         //p[this.queryParam] = q;
23144         if(this.pageSize){
23145             p.start = 0;
23146             p.limit = this.pageSize;
23147         }
23148         return p;
23149     },
23150
23151     /**
23152      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23153      */
23154     collapse : function(){
23155         if(!this.isExpanded()){
23156             return;
23157         }
23158         this.list.hide();
23159         Roo.get(document).un('mousedown', this.collapseIf, this);
23160         Roo.get(document).un('mousewheel', this.collapseIf, this);
23161         this.fireEvent('collapse', this);
23162     },
23163
23164     // private
23165     collapseIf : function(e){
23166         if(!e.within(this.wrap) && !e.within(this.list)){
23167             this.collapse();
23168         }
23169     },
23170
23171     /**
23172      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23173      */
23174     expand : function(){
23175         if(this.isExpanded() || !this.hasFocus){
23176             return;
23177         }
23178         this.list.alignTo(this.el, this.listAlign);
23179         this.list.show();
23180         Roo.get(document).on('mousedown', this.collapseIf, this);
23181         Roo.get(document).on('mousewheel', this.collapseIf, this);
23182         this.fireEvent('expand', this);
23183     },
23184
23185     // private
23186     // Implements the default empty TriggerField.onTriggerClick function
23187     onTriggerClick : function(){
23188         if(this.disabled){
23189             return;
23190         }
23191         if(this.isExpanded()){
23192             this.collapse();
23193             if (!this.blockFocus) {
23194                 this.el.focus();
23195             }
23196             
23197         }else {
23198             this.hasFocus = true;
23199             if(this.triggerAction == 'all') {
23200                 this.doQuery(this.allQuery, true);
23201             } else {
23202                 this.doQuery(this.getRawValue());
23203             }
23204             if (!this.blockFocus) {
23205                 this.el.focus();
23206             }
23207         }
23208     }
23209
23210     /** 
23211     * @cfg {Boolean} grow 
23212     * @hide 
23213     */
23214     /** 
23215     * @cfg {Number} growMin 
23216     * @hide 
23217     */
23218     /** 
23219     * @cfg {Number} growMax 
23220     * @hide 
23221     */
23222     /**
23223      * @hide
23224      * @method autoSize
23225      */
23226 });/*
23227  * Based on:
23228  * Ext JS Library 1.1.1
23229  * Copyright(c) 2006-2007, Ext JS, LLC.
23230  *
23231  * Originally Released Under LGPL - original licence link has changed is not relivant.
23232  *
23233  * Fork - LGPL
23234  * <script type="text/javascript">
23235  */
23236 /**
23237  * @class Roo.form.Checkbox
23238  * @extends Roo.form.Field
23239  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23240  * @constructor
23241  * Creates a new Checkbox
23242  * @param {Object} config Configuration options
23243  */
23244 Roo.form.Checkbox = function(config){
23245     Roo.form.Checkbox.superclass.constructor.call(this, config);
23246     this.addEvents({
23247         /**
23248          * @event check
23249          * Fires when the checkbox is checked or unchecked.
23250              * @param {Roo.form.Checkbox} this This checkbox
23251              * @param {Boolean} checked The new checked value
23252              */
23253         check : true
23254     });
23255 };
23256
23257 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23258     /**
23259      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23260      */
23261     focusClass : undefined,
23262     /**
23263      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23264      */
23265     fieldClass: "x-form-field",
23266     /**
23267      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23268      */
23269     checked: false,
23270     /**
23271      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23272      * {tag: "input", type: "checkbox", autocomplete: "off"})
23273      */
23274     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23275     /**
23276      * @cfg {String} boxLabel The text that appears beside the checkbox
23277      */
23278     boxLabel : "",
23279     /**
23280      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23281      */  
23282     inputValue : '1',
23283     /**
23284      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23285      */
23286      valueOff: '0', // value when not checked..
23287
23288     actionMode : 'viewEl', 
23289     //
23290     // private
23291     itemCls : 'x-menu-check-item x-form-item',
23292     groupClass : 'x-menu-group-item',
23293     inputType : 'hidden',
23294     
23295     
23296     inSetChecked: false, // check that we are not calling self...
23297     
23298     inputElement: false, // real input element?
23299     basedOn: false, // ????
23300     
23301     isFormField: true, // not sure where this is needed!!!!
23302
23303     onResize : function(){
23304         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23305         if(!this.boxLabel){
23306             this.el.alignTo(this.wrap, 'c-c');
23307         }
23308     },
23309
23310     initEvents : function(){
23311         Roo.form.Checkbox.superclass.initEvents.call(this);
23312         this.el.on("click", this.onClick,  this);
23313         this.el.on("change", this.onClick,  this);
23314     },
23315
23316
23317     getResizeEl : function(){
23318         return this.wrap;
23319     },
23320
23321     getPositionEl : function(){
23322         return this.wrap;
23323     },
23324
23325     // private
23326     onRender : function(ct, position){
23327         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23328         /*
23329         if(this.inputValue !== undefined){
23330             this.el.dom.value = this.inputValue;
23331         }
23332         */
23333         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23334         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23335         var viewEl = this.wrap.createChild({ 
23336             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23337         this.viewEl = viewEl;   
23338         this.wrap.on('click', this.onClick,  this); 
23339         
23340         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23341         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23342         
23343         
23344         
23345         if(this.boxLabel){
23346             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23347         //    viewEl.on('click', this.onClick,  this); 
23348         }
23349         //if(this.checked){
23350             this.setChecked(this.checked);
23351         //}else{
23352             //this.checked = this.el.dom;
23353         //}
23354
23355     },
23356
23357     // private
23358     initValue : Roo.emptyFn,
23359
23360     /**
23361      * Returns the checked state of the checkbox.
23362      * @return {Boolean} True if checked, else false
23363      */
23364     getValue : function(){
23365         if(this.el){
23366             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23367         }
23368         return this.valueOff;
23369         
23370     },
23371
23372         // private
23373     onClick : function(){ 
23374         this.setChecked(!this.checked);
23375
23376         //if(this.el.dom.checked != this.checked){
23377         //    this.setValue(this.el.dom.checked);
23378        // }
23379     },
23380
23381     /**
23382      * Sets the checked state of the checkbox.
23383      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
23384      */
23385     setValue : function(v,suppressEvent){
23386         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23387         //if(this.el && this.el.dom){
23388         //    this.el.dom.checked = this.checked;
23389         //    this.el.dom.defaultChecked = this.checked;
23390         //}
23391         this.setChecked(v === this.inputValue);
23392         //this.fireEvent("check", this, this.checked);
23393     },
23394     // private..
23395     setChecked : function(state,suppressEvent)
23396     {
23397         if (this.inSetChecked) {
23398             this.checked = state;
23399             return;
23400         }
23401         
23402     
23403         if(this.wrap){
23404             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23405         }
23406         this.checked = state;
23407         if(suppressEvent !== true){
23408             this.fireEvent('checkchange', this, state);
23409         }
23410         this.inSetChecked = true;
23411         this.el.dom.value = state ? this.inputValue : this.valueOff;
23412         this.inSetChecked = false;
23413         
23414     },
23415     // handle setting of hidden value by some other method!!?!?
23416     setFromHidden: function()
23417     {
23418         if(!this.el){
23419             return;
23420         }
23421         //console.log("SET FROM HIDDEN");
23422         //alert('setFrom hidden');
23423         this.setValue(this.el.dom.value);
23424     },
23425     
23426     onDestroy : function()
23427     {
23428         if(this.viewEl){
23429             Roo.get(this.viewEl).remove();
23430         }
23431          
23432         Roo.form.Checkbox.superclass.onDestroy.call(this);
23433     }
23434
23435 });/*
23436  * Based on:
23437  * Ext JS Library 1.1.1
23438  * Copyright(c) 2006-2007, Ext JS, LLC.
23439  *
23440  * Originally Released Under LGPL - original licence link has changed is not relivant.
23441  *
23442  * Fork - LGPL
23443  * <script type="text/javascript">
23444  */
23445  
23446 /**
23447  * @class Roo.form.Radio
23448  * @extends Roo.form.Checkbox
23449  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23450  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23451  * @constructor
23452  * Creates a new Radio
23453  * @param {Object} config Configuration options
23454  */
23455 Roo.form.Radio = function(){
23456     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23457 };
23458 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23459     inputType: 'radio',
23460
23461     /**
23462      * If this radio is part of a group, it will return the selected value
23463      * @return {String}
23464      */
23465     getGroupValue : function(){
23466         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23467     }
23468 });//<script type="text/javascript">
23469
23470 /*
23471  * Ext JS Library 1.1.1
23472  * Copyright(c) 2006-2007, Ext JS, LLC.
23473  * licensing@extjs.com
23474  * 
23475  * http://www.extjs.com/license
23476  */
23477  
23478  /*
23479   * 
23480   * Known bugs:
23481   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23482   * - IE ? - no idea how much works there.
23483   * 
23484   * 
23485   * 
23486   */
23487  
23488
23489 /**
23490  * @class Ext.form.HtmlEditor
23491  * @extends Ext.form.Field
23492  * Provides a lightweight HTML Editor component.
23493  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23494  * 
23495  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23496  * supported by this editor.</b><br/><br/>
23497  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23498  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23499  */
23500 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23501       /**
23502      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23503      */
23504     toolbars : false,
23505     /**
23506      * @cfg {String} createLinkText The default text for the create link prompt
23507      */
23508     createLinkText : 'Please enter the URL for the link:',
23509     /**
23510      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23511      */
23512     defaultLinkValue : 'http:/'+'/',
23513    
23514     
23515     // id of frame..
23516     frameId: false,
23517     
23518     // private properties
23519     validationEvent : false,
23520     deferHeight: true,
23521     initialized : false,
23522     activated : false,
23523     sourceEditMode : false,
23524     onFocus : Roo.emptyFn,
23525     iframePad:3,
23526     hideMode:'offsets',
23527     defaultAutoCreate : {
23528         tag: "textarea",
23529         style:"width:500px;height:300px;",
23530         autocomplete: "off"
23531     },
23532
23533     // private
23534     initComponent : function(){
23535         this.addEvents({
23536             /**
23537              * @event initialize
23538              * Fires when the editor is fully initialized (including the iframe)
23539              * @param {HtmlEditor} this
23540              */
23541             initialize: true,
23542             /**
23543              * @event activate
23544              * Fires when the editor is first receives the focus. Any insertion must wait
23545              * until after this event.
23546              * @param {HtmlEditor} this
23547              */
23548             activate: true,
23549              /**
23550              * @event beforesync
23551              * Fires before the textarea is updated with content from the editor iframe. Return false
23552              * to cancel the sync.
23553              * @param {HtmlEditor} this
23554              * @param {String} html
23555              */
23556             beforesync: true,
23557              /**
23558              * @event beforepush
23559              * Fires before the iframe editor is updated with content from the textarea. Return false
23560              * to cancel the push.
23561              * @param {HtmlEditor} this
23562              * @param {String} html
23563              */
23564             beforepush: true,
23565              /**
23566              * @event sync
23567              * Fires when the textarea is updated with content from the editor iframe.
23568              * @param {HtmlEditor} this
23569              * @param {String} html
23570              */
23571             sync: true,
23572              /**
23573              * @event push
23574              * Fires when the iframe editor is updated with content from the textarea.
23575              * @param {HtmlEditor} this
23576              * @param {String} html
23577              */
23578             push: true,
23579              /**
23580              * @event editmodechange
23581              * Fires when the editor switches edit modes
23582              * @param {HtmlEditor} this
23583              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23584              */
23585             editmodechange: true,
23586             /**
23587              * @event editorevent
23588              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23589              * @param {HtmlEditor} this
23590              */
23591             editorevent: true
23592         })
23593     },
23594
23595     /**
23596      * Protected method that will not generally be called directly. It
23597      * is called when the editor creates its toolbar. Override this method if you need to
23598      * add custom toolbar buttons.
23599      * @param {HtmlEditor} editor
23600      */
23601     createToolbar : function(editor){
23602         if (!editor.toolbars || !editor.toolbars.length) {
23603             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23604         }
23605         
23606         for (var i =0 ; i < editor.toolbars.length;i++) {
23607             editor.toolbars[i].init(editor);
23608         }
23609          
23610         
23611     },
23612
23613     /**
23614      * Protected method that will not generally be called directly. It
23615      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23616      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23617      */
23618     getDocMarkup : function(){
23619         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23620     },
23621
23622     // private
23623     onRender : function(ct, position){
23624         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23625         this.el.dom.style.border = '0 none';
23626         this.el.dom.setAttribute('tabIndex', -1);
23627         this.el.addClass('x-hidden');
23628         if(Roo.isIE){ // fix IE 1px bogus margin
23629             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23630         }
23631         this.wrap = this.el.wrap({
23632             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23633         });
23634
23635         this.frameId = Roo.id();
23636         this.createToolbar(this);
23637         
23638         
23639         
23640         
23641       
23642         
23643         var iframe = this.wrap.createChild({
23644             tag: 'iframe',
23645             id: this.frameId,
23646             name: this.frameId,
23647             frameBorder : 'no',
23648             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23649         });
23650         
23651        // console.log(iframe);
23652         //this.wrap.dom.appendChild(iframe);
23653
23654         this.iframe = iframe.dom;
23655
23656          this.assignDocWin();
23657         
23658         this.doc.designMode = 'on';
23659        
23660         this.doc.open();
23661         this.doc.write(this.getDocMarkup());
23662         this.doc.close();
23663
23664         
23665         var task = { // must defer to wait for browser to be ready
23666             run : function(){
23667                 //console.log("run task?" + this.doc.readyState);
23668                 this.assignDocWin();
23669                 if(this.doc.body || this.doc.readyState == 'complete'){
23670                     try {
23671                         
23672                        
23673                         this.doc.designMode="on";
23674                     } catch (e) {
23675                         return;
23676                     }
23677                     Roo.TaskMgr.stop(task);
23678                     this.initEditor.defer(10, this);
23679                 }
23680             },
23681             interval : 10,
23682             duration:10000,
23683             scope: this
23684         };
23685         Roo.TaskMgr.start(task);
23686
23687         if(!this.width){
23688             this.setSize(this.el.getSize());
23689         }
23690     },
23691
23692     // private
23693     onResize : function(w, h){
23694         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23695         if(this.el && this.iframe){
23696             if(typeof w == 'number'){
23697                 var aw = w - this.wrap.getFrameWidth('lr');
23698                 this.el.setWidth(this.adjustWidth('textarea', aw));
23699                 this.iframe.style.width = aw + 'px';
23700             }
23701             if(typeof h == 'number'){
23702                 var tbh = 0;
23703                 for (var i =0; i < this.toolbars.length;i++) {
23704                     // fixme - ask toolbars for heights?
23705                     tbh += this.toolbars[i].tb.el.getHeight();
23706                 }
23707                 
23708                 
23709                 
23710                 
23711                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23712                 this.el.setHeight(this.adjustWidth('textarea', ah));
23713                 this.iframe.style.height = ah + 'px';
23714                 if(this.doc){
23715                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
23716                 }
23717             }
23718         }
23719     },
23720
23721     /**
23722      * Toggles the editor between standard and source edit mode.
23723      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23724      */
23725     toggleSourceEdit : function(sourceEditMode){
23726         
23727         this.sourceEditMode = sourceEditMode === true;
23728         
23729         if(this.sourceEditMode){
23730           
23731             this.syncValue();
23732             this.iframe.className = 'x-hidden';
23733             this.el.removeClass('x-hidden');
23734             this.el.dom.removeAttribute('tabIndex');
23735             this.el.focus();
23736         }else{
23737              
23738             this.pushValue();
23739             this.iframe.className = '';
23740             this.el.addClass('x-hidden');
23741             this.el.dom.setAttribute('tabIndex', -1);
23742             this.deferFocus();
23743         }
23744         this.setSize(this.wrap.getSize());
23745         this.fireEvent('editmodechange', this, this.sourceEditMode);
23746     },
23747
23748     // private used internally
23749     createLink : function(){
23750         var url = prompt(this.createLinkText, this.defaultLinkValue);
23751         if(url && url != 'http:/'+'/'){
23752             this.relayCmd('createlink', url);
23753         }
23754     },
23755
23756     // private (for BoxComponent)
23757     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23758
23759     // private (for BoxComponent)
23760     getResizeEl : function(){
23761         return this.wrap;
23762     },
23763
23764     // private (for BoxComponent)
23765     getPositionEl : function(){
23766         return this.wrap;
23767     },
23768
23769     // private
23770     initEvents : function(){
23771         this.originalValue = this.getValue();
23772     },
23773
23774     /**
23775      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23776      * @method
23777      */
23778     markInvalid : Roo.emptyFn,
23779     /**
23780      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23781      * @method
23782      */
23783     clearInvalid : Roo.emptyFn,
23784
23785     setValue : function(v){
23786         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23787         this.pushValue();
23788     },
23789
23790     /**
23791      * Protected method that will not generally be called directly. If you need/want
23792      * custom HTML cleanup, this is the method you should override.
23793      * @param {String} html The HTML to be cleaned
23794      * return {String} The cleaned HTML
23795      */
23796     cleanHtml : function(html){
23797         html = String(html);
23798         if(html.length > 5){
23799             if(Roo.isSafari){ // strip safari nonsense
23800                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23801             }
23802         }
23803         if(html == '&nbsp;'){
23804             html = '';
23805         }
23806         return html;
23807     },
23808
23809     /**
23810      * Protected method that will not generally be called directly. Syncs the contents
23811      * of the editor iframe with the textarea.
23812      */
23813     syncValue : function(){
23814         if(this.initialized){
23815             var bd = (this.doc.body || this.doc.documentElement);
23816             var html = bd.innerHTML;
23817             if(Roo.isSafari){
23818                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23819                 var m = bs.match(/text-align:(.*?);/i);
23820                 if(m && m[1]){
23821                     html = '<div style="'+m[0]+'">' + html + '</div>';
23822                 }
23823             }
23824             html = this.cleanHtml(html);
23825             if(this.fireEvent('beforesync', this, html) !== false){
23826                 this.el.dom.value = html;
23827                 this.fireEvent('sync', this, html);
23828             }
23829         }
23830     },
23831
23832     /**
23833      * Protected method that will not generally be called directly. Pushes the value of the textarea
23834      * into the iframe editor.
23835      */
23836     pushValue : function(){
23837         if(this.initialized){
23838             var v = this.el.dom.value;
23839             if(v.length < 1){
23840                 v = '&#160;';
23841             }
23842             if(this.fireEvent('beforepush', this, v) !== false){
23843                 (this.doc.body || this.doc.documentElement).innerHTML = v;
23844                 this.fireEvent('push', this, v);
23845             }
23846         }
23847     },
23848
23849     // private
23850     deferFocus : function(){
23851         this.focus.defer(10, this);
23852     },
23853
23854     // doc'ed in Field
23855     focus : function(){
23856         if(this.win && !this.sourceEditMode){
23857             this.win.focus();
23858         }else{
23859             this.el.focus();
23860         }
23861     },
23862     
23863     assignDocWin: function()
23864     {
23865         var iframe = this.iframe;
23866         
23867          if(Roo.isIE){
23868             this.doc = iframe.contentWindow.document;
23869             this.win = iframe.contentWindow;
23870         } else {
23871             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23872             this.win = Roo.get(this.frameId).dom.contentWindow;
23873         }
23874     },
23875     
23876     // private
23877     initEditor : function(){
23878         //console.log("INIT EDITOR");
23879         this.assignDocWin();
23880         
23881         
23882         
23883         this.doc.designMode="on";
23884         this.doc.open();
23885         this.doc.write(this.getDocMarkup());
23886         this.doc.close();
23887         
23888         var dbody = (this.doc.body || this.doc.documentElement);
23889         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23890         // this copies styles from the containing element into thsi one..
23891         // not sure why we need all of this..
23892         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23893         ss['background-attachment'] = 'fixed'; // w3c
23894         dbody.bgProperties = 'fixed'; // ie
23895         Roo.DomHelper.applyStyles(dbody, ss);
23896         Roo.EventManager.on(this.doc, {
23897             'mousedown': this.onEditorEvent,
23898             'dblclick': this.onEditorEvent,
23899             'click': this.onEditorEvent,
23900             'keyup': this.onEditorEvent,
23901             buffer:100,
23902             scope: this
23903         });
23904         if(Roo.isGecko){
23905             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
23906         }
23907         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23908             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23909         }
23910         this.initialized = true;
23911
23912         this.fireEvent('initialize', this);
23913         this.pushValue();
23914     },
23915
23916     // private
23917     onDestroy : function(){
23918         
23919         
23920         
23921         if(this.rendered){
23922             
23923             for (var i =0; i < this.toolbars.length;i++) {
23924                 // fixme - ask toolbars for heights?
23925                 this.toolbars[i].onDestroy();
23926             }
23927             
23928             this.wrap.dom.innerHTML = '';
23929             this.wrap.remove();
23930         }
23931     },
23932
23933     // private
23934     onFirstFocus : function(){
23935         
23936         this.assignDocWin();
23937         
23938         
23939         this.activated = true;
23940         for (var i =0; i < this.toolbars.length;i++) {
23941             this.toolbars[i].onFirstFocus();
23942         }
23943        
23944         if(Roo.isGecko){ // prevent silly gecko errors
23945             this.win.focus();
23946             var s = this.win.getSelection();
23947             if(!s.focusNode || s.focusNode.nodeType != 3){
23948                 var r = s.getRangeAt(0);
23949                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23950                 r.collapse(true);
23951                 this.deferFocus();
23952             }
23953             try{
23954                 this.execCmd('useCSS', true);
23955                 this.execCmd('styleWithCSS', false);
23956             }catch(e){}
23957         }
23958         this.fireEvent('activate', this);
23959     },
23960
23961     // private
23962     adjustFont: function(btn){
23963         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23964         //if(Roo.isSafari){ // safari
23965         //    adjust *= 2;
23966        // }
23967         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23968         if(Roo.isSafari){ // safari
23969             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23970             v =  (v < 10) ? 10 : v;
23971             v =  (v > 48) ? 48 : v;
23972             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23973             
23974         }
23975         
23976         
23977         v = Math.max(1, v+adjust);
23978         
23979         this.execCmd('FontSize', v  );
23980     },
23981
23982     onEditorEvent : function(e){
23983         this.fireEvent('editorevent', this, e);
23984       //  this.updateToolbar();
23985         this.syncValue();
23986     },
23987
23988     insertTag : function(tg)
23989     {
23990         // could be a bit smarter... -> wrap the current selected tRoo..
23991         
23992         this.execCmd("formatblock",   tg);
23993         
23994     },
23995     
23996     insertText : function(txt)
23997     {
23998         
23999         
24000         range = this.createRange();
24001         range.deleteContents();
24002                //alert(Sender.getAttribute('label'));
24003                
24004         range.insertNode(this.doc.createTextNode(txt));
24005     } ,
24006     
24007     // private
24008     relayBtnCmd : function(btn){
24009         this.relayCmd(btn.cmd);
24010     },
24011
24012     /**
24013      * Executes a Midas editor command on the editor document and performs necessary focus and
24014      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24015      * @param {String} cmd The Midas command
24016      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24017      */
24018     relayCmd : function(cmd, value){
24019         this.win.focus();
24020         this.execCmd(cmd, value);
24021         this.fireEvent('editorevent', this);
24022         //this.updateToolbar();
24023         this.deferFocus();
24024     },
24025
24026     /**
24027      * Executes a Midas editor command directly on the editor document.
24028      * For visual commands, you should use {@link #relayCmd} instead.
24029      * <b>This should only be called after the editor is initialized.</b>
24030      * @param {String} cmd The Midas command
24031      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24032      */
24033     execCmd : function(cmd, value){
24034         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24035         this.syncValue();
24036     },
24037
24038     // private
24039     applyCommand : function(e){
24040         if(e.ctrlKey){
24041             var c = e.getCharCode(), cmd;
24042             if(c > 0){
24043                 c = String.fromCharCode(c);
24044                 switch(c){
24045                     case 'b':
24046                         cmd = 'bold';
24047                     break;
24048                     case 'i':
24049                         cmd = 'italic';
24050                     break;
24051                     case 'u':
24052                         cmd = 'underline';
24053                     break;
24054                 }
24055                 if(cmd){
24056                     this.win.focus();
24057                     this.execCmd(cmd);
24058                     this.deferFocus();
24059                     e.preventDefault();
24060                 }
24061             }
24062         }
24063     },
24064
24065     /**
24066      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24067      * to insert tRoo.
24068      * @param {String} text
24069      */
24070     insertAtCursor : function(text){
24071         if(!this.activated){
24072             return;
24073         }
24074         if(Roo.isIE){
24075             this.win.focus();
24076             var r = this.doc.selection.createRange();
24077             if(r){
24078                 r.collapse(true);
24079                 r.pasteHTML(text);
24080                 this.syncValue();
24081                 this.deferFocus();
24082             }
24083         }else if(Roo.isGecko || Roo.isOpera){
24084             this.win.focus();
24085             this.execCmd('InsertHTML', text);
24086             this.deferFocus();
24087         }else if(Roo.isSafari){
24088             this.execCmd('InsertText', text);
24089             this.deferFocus();
24090         }
24091     },
24092
24093     // private
24094     fixKeys : function(){ // load time branching for fastest keydown performance
24095         if(Roo.isIE){
24096             return function(e){
24097                 var k = e.getKey(), r;
24098                 if(k == e.TAB){
24099                     e.stopEvent();
24100                     r = this.doc.selection.createRange();
24101                     if(r){
24102                         r.collapse(true);
24103                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24104                         this.deferFocus();
24105                     }
24106                 }else if(k == e.ENTER){
24107                     r = this.doc.selection.createRange();
24108                     if(r){
24109                         var target = r.parentElement();
24110                         if(!target || target.tagName.toLowerCase() != 'li'){
24111                             e.stopEvent();
24112                             r.pasteHTML('<br />');
24113                             r.collapse(false);
24114                             r.select();
24115                         }
24116                     }
24117                 }
24118             };
24119         }else if(Roo.isOpera){
24120             return function(e){
24121                 var k = e.getKey();
24122                 if(k == e.TAB){
24123                     e.stopEvent();
24124                     this.win.focus();
24125                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24126                     this.deferFocus();
24127                 }
24128             };
24129         }else if(Roo.isSafari){
24130             return function(e){
24131                 var k = e.getKey();
24132                 if(k == e.TAB){
24133                     e.stopEvent();
24134                     this.execCmd('InsertText','\t');
24135                     this.deferFocus();
24136                 }
24137              };
24138         }
24139     }(),
24140     
24141     getAllAncestors: function()
24142     {
24143         var p = this.getSelectedNode();
24144         var a = [];
24145         if (!p) {
24146             a.push(p); // push blank onto stack..
24147             p = this.getParentElement();
24148         }
24149         
24150         
24151         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24152             a.push(p);
24153             p = p.parentNode;
24154         }
24155         a.push(this.doc.body);
24156         return a;
24157     },
24158     lastSel : false,
24159     lastSelNode : false,
24160     
24161     
24162     getSelection : function() 
24163     {
24164         this.assignDocWin();
24165         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24166     },
24167     
24168     getSelectedNode: function() 
24169     {
24170         // this may only work on Gecko!!!
24171         
24172         // should we cache this!!!!
24173         
24174         
24175         
24176          
24177         var range = this.createRange(this.getSelection());
24178         
24179         if (Roo.isIE) {
24180             var parent = range.parentElement();
24181             while (true) {
24182                 var testRange = range.duplicate();
24183                 testRange.moveToElementText(parent);
24184                 if (testRange.inRange(range)) {
24185                     break;
24186                 }
24187                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24188                     break;
24189                 }
24190                 parent = parent.parentElement;
24191             }
24192             return parent;
24193         }
24194         
24195         
24196         var ar = range.endContainer.childNodes;
24197         if (!ar.length) {
24198             ar = range.commonAncestorContainer.childNodes;
24199             //alert(ar.length);
24200         }
24201         var nodes = [];
24202         var other_nodes = [];
24203         var has_other_nodes = false;
24204         for (var i=0;i<ar.length;i++) {
24205             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24206                 continue;
24207             }
24208             // fullly contained node.
24209             
24210             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24211                 nodes.push(ar[i]);
24212                 continue;
24213             }
24214             
24215             // probably selected..
24216             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24217                 other_nodes.push(ar[i]);
24218                 continue;
24219             }
24220             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24221                 continue;
24222             }
24223             
24224             
24225             has_other_nodes = true;
24226         }
24227         if (!nodes.length && other_nodes.length) {
24228             nodes= other_nodes;
24229         }
24230         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24231             return false;
24232         }
24233         
24234         return nodes[0];
24235     },
24236     createRange: function(sel)
24237     {
24238         // this has strange effects when using with 
24239         // top toolbar - not sure if it's a great idea.
24240         //this.editor.contentWindow.focus();
24241         if (typeof sel != "undefined") {
24242             try {
24243                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24244             } catch(e) {
24245                 return this.doc.createRange();
24246             }
24247         } else {
24248             return this.doc.createRange();
24249         }
24250     },
24251     getParentElement: function()
24252     {
24253         
24254         this.assignDocWin();
24255         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24256         
24257         var range = this.createRange(sel);
24258          
24259         try {
24260             var p = range.commonAncestorContainer;
24261             while (p.nodeType == 3) { // text node
24262                 p = p.parentNode;
24263             }
24264             return p;
24265         } catch (e) {
24266             return null;
24267         }
24268     
24269     },
24270     
24271     
24272     
24273     // BC Hacks - cause I cant work out what i was trying to do..
24274     rangeIntersectsNode : function(range, node)
24275     {
24276         var nodeRange = node.ownerDocument.createRange();
24277         try {
24278             nodeRange.selectNode(node);
24279         }
24280         catch (e) {
24281             nodeRange.selectNodeContents(node);
24282         }
24283
24284         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24285                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24286     },
24287     rangeCompareNode : function(range, node) {
24288         var nodeRange = node.ownerDocument.createRange();
24289         try {
24290             nodeRange.selectNode(node);
24291         } catch (e) {
24292             nodeRange.selectNodeContents(node);
24293         }
24294         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24295         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24296
24297         if (nodeIsBefore && !nodeIsAfter)
24298             return 0;
24299         if (!nodeIsBefore && nodeIsAfter)
24300             return 1;
24301         if (nodeIsBefore && nodeIsAfter)
24302             return 2;
24303
24304         return 3;
24305     }
24306
24307     
24308     
24309     // hide stuff that is not compatible
24310     /**
24311      * @event blur
24312      * @hide
24313      */
24314     /**
24315      * @event change
24316      * @hide
24317      */
24318     /**
24319      * @event focus
24320      * @hide
24321      */
24322     /**
24323      * @event specialkey
24324      * @hide
24325      */
24326     /**
24327      * @cfg {String} fieldClass @hide
24328      */
24329     /**
24330      * @cfg {String} focusClass @hide
24331      */
24332     /**
24333      * @cfg {String} autoCreate @hide
24334      */
24335     /**
24336      * @cfg {String} inputType @hide
24337      */
24338     /**
24339      * @cfg {String} invalidClass @hide
24340      */
24341     /**
24342      * @cfg {String} invalidText @hide
24343      */
24344     /**
24345      * @cfg {String} msgFx @hide
24346      */
24347     /**
24348      * @cfg {String} validateOnBlur @hide
24349      */
24350 });// <script type="text/javascript">
24351 /*
24352  * Based on
24353  * Ext JS Library 1.1.1
24354  * Copyright(c) 2006-2007, Ext JS, LLC.
24355  *  
24356  
24357  */
24358
24359 /**
24360  * @class Roo.form.HtmlEditorToolbar1
24361  * Basic Toolbar
24362  * 
24363  * Usage:
24364  *
24365  new Roo.form.HtmlEditor({
24366     ....
24367     toolbars : [
24368         new Roo.form.HtmlEditorToolbar1({
24369             disable : { fonts: 1 , format: 1, ..., ... , ...],
24370             btns : [ .... ]
24371         })
24372     }
24373      
24374  * 
24375  * @cfg {Object} disable List of elements to disable..
24376  * @cfg {Array} btns List of additional buttons.
24377  * 
24378  * 
24379  * NEEDS Extra CSS? 
24380  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24381  */
24382  
24383 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24384 {
24385     
24386     Roo.apply(this, config);
24387     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24388     // dont call parent... till later.
24389 }
24390
24391 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24392     
24393     tb: false,
24394     
24395     rendered: false,
24396     
24397     editor : false,
24398     /**
24399      * @cfg {Object} disable  List of toolbar elements to disable
24400          
24401      */
24402     disable : false,
24403       /**
24404      * @cfg {Array} fontFamilies An array of available font families
24405      */
24406     fontFamilies : [
24407         'Arial',
24408         'Courier New',
24409         'Tahoma',
24410         'Times New Roman',
24411         'Verdana'
24412     ],
24413     
24414     specialChars : [
24415            "&#169;",
24416           "&#174;",     
24417           "&#8482;",    
24418           "&#163;" ,    
24419          // "&#8212;",    
24420           "&#8230;",    
24421           "&#247;" ,    
24422         //  "&#225;" ,     ?? a acute?
24423            "&#8364;"    , //Euro
24424        //   "&#8220;"    ,
24425         //  "&#8221;"    ,
24426         //  "&#8226;"    ,
24427           "&#176;"  //   , // degrees
24428
24429          // "&#233;"     , // e ecute
24430          // "&#250;"     , // u ecute?
24431     ],
24432     inputElements : [ 
24433             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24434             "input:submit", "input:button", "select", "textarea", "label" ],
24435     formats : [
24436         ["p"] ,  
24437         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24438         ["pre"],[ "code"], 
24439         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24440     ],
24441      /**
24442      * @cfg {String} defaultFont default font to use.
24443      */
24444     defaultFont: 'tahoma',
24445    
24446     fontSelect : false,
24447     
24448     
24449     formatCombo : false,
24450     
24451     init : function(editor)
24452     {
24453         this.editor = editor;
24454         
24455         
24456         var fid = editor.frameId;
24457         var etb = this;
24458         function btn(id, toggle, handler){
24459             var xid = fid + '-'+ id ;
24460             return {
24461                 id : xid,
24462                 cmd : id,
24463                 cls : 'x-btn-icon x-edit-'+id,
24464                 enableToggle:toggle !== false,
24465                 scope: editor, // was editor...
24466                 handler:handler||editor.relayBtnCmd,
24467                 clickEvent:'mousedown',
24468                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24469                 tabIndex:-1
24470             };
24471         }
24472         
24473         
24474         
24475         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24476         this.tb = tb;
24477          // stop form submits
24478         tb.el.on('click', function(e){
24479             e.preventDefault(); // what does this do?
24480         });
24481
24482         if(!this.disable.font && !Roo.isSafari){
24483             /* why no safari for fonts
24484             editor.fontSelect = tb.el.createChild({
24485                 tag:'select',
24486                 tabIndex: -1,
24487                 cls:'x-font-select',
24488                 html: editor.createFontOptions()
24489             });
24490             editor.fontSelect.on('change', function(){
24491                 var font = editor.fontSelect.dom.value;
24492                 editor.relayCmd('fontname', font);
24493                 editor.deferFocus();
24494             }, editor);
24495             tb.add(
24496                 editor.fontSelect.dom,
24497                 '-'
24498             );
24499             */
24500         };
24501         if(!this.disable.formats){
24502             this.formatCombo = new Roo.form.ComboBox({
24503                 store: new Roo.data.SimpleStore({
24504                     id : 'tag',
24505                     fields: ['tag'],
24506                     data : this.formats // from states.js
24507                 }),
24508                 blockFocus : true,
24509                 //autoCreate : {tag: "div",  size: "20"},
24510                 displayField:'tag',
24511                 typeAhead: false,
24512                 mode: 'local',
24513                 editable : false,
24514                 triggerAction: 'all',
24515                 emptyText:'Add tag',
24516                 selectOnFocus:true,
24517                 width:135,
24518                 listeners : {
24519                     'select': function(c, r, i) {
24520                         editor.insertTag(r.get('tag'));
24521                         editor.focus();
24522                     }
24523                 }
24524
24525             });
24526             tb.addField(this.formatCombo);
24527             
24528         }
24529         
24530         if(!this.disable.format){
24531             tb.add(
24532                 btn('bold'),
24533                 btn('italic'),
24534                 btn('underline')
24535             );
24536         };
24537         if(!this.disable.fontSize){
24538             tb.add(
24539                 '-',
24540                 
24541                 
24542                 btn('increasefontsize', false, editor.adjustFont),
24543                 btn('decreasefontsize', false, editor.adjustFont)
24544             );
24545         };
24546         
24547         
24548         if(this.disable.colors){
24549             tb.add(
24550                 '-', {
24551                     id:editor.frameId +'-forecolor',
24552                     cls:'x-btn-icon x-edit-forecolor',
24553                     clickEvent:'mousedown',
24554                     tooltip: this.buttonTips['forecolor'] || undefined,
24555                     tabIndex:-1,
24556                     menu : new Roo.menu.ColorMenu({
24557                         allowReselect: true,
24558                         focus: Roo.emptyFn,
24559                         value:'000000',
24560                         plain:true,
24561                         selectHandler: function(cp, color){
24562                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24563                             editor.deferFocus();
24564                         },
24565                         scope: editor,
24566                         clickEvent:'mousedown'
24567                     })
24568                 }, {
24569                     id:editor.frameId +'backcolor',
24570                     cls:'x-btn-icon x-edit-backcolor',
24571                     clickEvent:'mousedown',
24572                     tooltip: this.buttonTips['backcolor'] || undefined,
24573                     tabIndex:-1,
24574                     menu : new Roo.menu.ColorMenu({
24575                         focus: Roo.emptyFn,
24576                         value:'FFFFFF',
24577                         plain:true,
24578                         allowReselect: true,
24579                         selectHandler: function(cp, color){
24580                             if(Roo.isGecko){
24581                                 editor.execCmd('useCSS', false);
24582                                 editor.execCmd('hilitecolor', color);
24583                                 editor.execCmd('useCSS', true);
24584                                 editor.deferFocus();
24585                             }else{
24586                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
24587                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
24588                                 editor.deferFocus();
24589                             }
24590                         },
24591                         scope:editor,
24592                         clickEvent:'mousedown'
24593                     })
24594                 }
24595             );
24596         };
24597         // now add all the items...
24598         
24599
24600         if(!this.disable.alignments){
24601             tb.add(
24602                 '-',
24603                 btn('justifyleft'),
24604                 btn('justifycenter'),
24605                 btn('justifyright')
24606             );
24607         };
24608
24609         //if(!Roo.isSafari){
24610             if(!this.disable.links){
24611                 tb.add(
24612                     '-',
24613                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
24614                 );
24615             };
24616
24617             if(!this.disable.lists){
24618                 tb.add(
24619                     '-',
24620                     btn('insertorderedlist'),
24621                     btn('insertunorderedlist')
24622                 );
24623             }
24624             if(!this.disable.sourceEdit){
24625                 tb.add(
24626                     '-',
24627                     btn('sourceedit', true, function(btn){
24628                         this.toggleSourceEdit(btn.pressed);
24629                     })
24630                 );
24631             }
24632         //}
24633         
24634         var smenu = { };
24635         // special menu.. - needs to be tidied up..
24636         if (!this.disable.special) {
24637             smenu = {
24638                 text: "&#169;",
24639                 cls: 'x-edit-none',
24640                 menu : {
24641                     items : []
24642                    }
24643             };
24644             for (var i =0; i < this.specialChars.length; i++) {
24645                 smenu.menu.items.push({
24646                     
24647                     text: this.specialChars[i],
24648                     handler: function(a,b) {
24649                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
24650                     },
24651                     tabIndex:-1
24652                 });
24653             }
24654             
24655             
24656             tb.add(smenu);
24657             
24658             
24659         }
24660         if (this.btns) {
24661             for(var i =0; i< this.btns.length;i++) {
24662                 var b = this.btns[i];
24663                 b.cls =  'x-edit-none';
24664                 b.scope = editor;
24665                 tb.add(b);
24666             }
24667         
24668         }
24669         
24670         
24671         
24672         // disable everything...
24673         
24674         this.tb.items.each(function(item){
24675            if(item.id != editor.frameId+ '-sourceedit'){
24676                 item.disable();
24677             }
24678         });
24679         this.rendered = true;
24680         
24681         // the all the btns;
24682         editor.on('editorevent', this.updateToolbar, this);
24683         // other toolbars need to implement this..
24684         //editor.on('editmodechange', this.updateToolbar, this);
24685     },
24686     
24687     
24688     
24689     /**
24690      * Protected method that will not generally be called directly. It triggers
24691      * a toolbar update by reading the markup state of the current selection in the editor.
24692      */
24693     updateToolbar: function(){
24694
24695         if(!this.editor.activated){
24696             this.editor.onFirstFocus();
24697             return;
24698         }
24699
24700         var btns = this.tb.items.map, 
24701             doc = this.editor.doc,
24702             frameId = this.editor.frameId;
24703
24704         if(!this.disable.font && !Roo.isSafari){
24705             /*
24706             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
24707             if(name != this.fontSelect.dom.value){
24708                 this.fontSelect.dom.value = name;
24709             }
24710             */
24711         }
24712         if(!this.disable.format){
24713             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
24714             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
24715             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
24716         }
24717         if(!this.disable.alignments){
24718             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
24719             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
24720             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
24721         }
24722         if(!Roo.isSafari && !this.disable.lists){
24723             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
24724             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
24725         }
24726         
24727         var ans = this.editor.getAllAncestors();
24728         if (this.formatCombo) {
24729             
24730             
24731             var store = this.formatCombo.store;
24732             this.formatCombo.setValue("");
24733             for (var i =0; i < ans.length;i++) {
24734                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
24735                     // select it..
24736                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24737                     break;
24738                 }
24739             }
24740         }
24741         
24742         
24743         
24744         // hides menus... - so this cant be on a menu...
24745         Roo.menu.MenuMgr.hideAll();
24746
24747         //this.editorsyncValue();
24748     },
24749    
24750     
24751     createFontOptions : function(){
24752         var buf = [], fs = this.fontFamilies, ff, lc;
24753         for(var i = 0, len = fs.length; i< len; i++){
24754             ff = fs[i];
24755             lc = ff.toLowerCase();
24756             buf.push(
24757                 '<option value="',lc,'" style="font-family:',ff,';"',
24758                     (this.defaultFont == lc ? ' selected="true">' : '>'),
24759                     ff,
24760                 '</option>'
24761             );
24762         }
24763         return buf.join('');
24764     },
24765     
24766     toggleSourceEdit : function(sourceEditMode){
24767         if(sourceEditMode === undefined){
24768             sourceEditMode = !this.sourceEditMode;
24769         }
24770         this.sourceEditMode = sourceEditMode === true;
24771         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
24772         // just toggle the button?
24773         if(btn.pressed !== this.editor.sourceEditMode){
24774             btn.toggle(this.editor.sourceEditMode);
24775             return;
24776         }
24777         
24778         if(this.sourceEditMode){
24779             this.tb.items.each(function(item){
24780                 if(item.cmd != 'sourceedit'){
24781                     item.disable();
24782                 }
24783             });
24784           
24785         }else{
24786             if(this.initialized){
24787                 this.tb.items.each(function(item){
24788                     item.enable();
24789                 });
24790             }
24791             
24792         }
24793         // tell the editor that it's been pressed..
24794         this.editor.toggleSourceEdit(sourceEditMode);
24795        
24796     },
24797      /**
24798      * Object collection of toolbar tooltips for the buttons in the editor. The key
24799      * is the command id associated with that button and the value is a valid QuickTips object.
24800      * For example:
24801 <pre><code>
24802 {
24803     bold : {
24804         title: 'Bold (Ctrl+B)',
24805         text: 'Make the selected text bold.',
24806         cls: 'x-html-editor-tip'
24807     },
24808     italic : {
24809         title: 'Italic (Ctrl+I)',
24810         text: 'Make the selected text italic.',
24811         cls: 'x-html-editor-tip'
24812     },
24813     ...
24814 </code></pre>
24815     * @type Object
24816      */
24817     buttonTips : {
24818         bold : {
24819             title: 'Bold (Ctrl+B)',
24820             text: 'Make the selected text bold.',
24821             cls: 'x-html-editor-tip'
24822         },
24823         italic : {
24824             title: 'Italic (Ctrl+I)',
24825             text: 'Make the selected text italic.',
24826             cls: 'x-html-editor-tip'
24827         },
24828         underline : {
24829             title: 'Underline (Ctrl+U)',
24830             text: 'Underline the selected text.',
24831             cls: 'x-html-editor-tip'
24832         },
24833         increasefontsize : {
24834             title: 'Grow Text',
24835             text: 'Increase the font size.',
24836             cls: 'x-html-editor-tip'
24837         },
24838         decreasefontsize : {
24839             title: 'Shrink Text',
24840             text: 'Decrease the font size.',
24841             cls: 'x-html-editor-tip'
24842         },
24843         backcolor : {
24844             title: 'Text Highlight Color',
24845             text: 'Change the background color of the selected text.',
24846             cls: 'x-html-editor-tip'
24847         },
24848         forecolor : {
24849             title: 'Font Color',
24850             text: 'Change the color of the selected text.',
24851             cls: 'x-html-editor-tip'
24852         },
24853         justifyleft : {
24854             title: 'Align Text Left',
24855             text: 'Align text to the left.',
24856             cls: 'x-html-editor-tip'
24857         },
24858         justifycenter : {
24859             title: 'Center Text',
24860             text: 'Center text in the editor.',
24861             cls: 'x-html-editor-tip'
24862         },
24863         justifyright : {
24864             title: 'Align Text Right',
24865             text: 'Align text to the right.',
24866             cls: 'x-html-editor-tip'
24867         },
24868         insertunorderedlist : {
24869             title: 'Bullet List',
24870             text: 'Start a bulleted list.',
24871             cls: 'x-html-editor-tip'
24872         },
24873         insertorderedlist : {
24874             title: 'Numbered List',
24875             text: 'Start a numbered list.',
24876             cls: 'x-html-editor-tip'
24877         },
24878         createlink : {
24879             title: 'Hyperlink',
24880             text: 'Make the selected text a hyperlink.',
24881             cls: 'x-html-editor-tip'
24882         },
24883         sourceedit : {
24884             title: 'Source Edit',
24885             text: 'Switch to source editing mode.',
24886             cls: 'x-html-editor-tip'
24887         }
24888     },
24889     // private
24890     onDestroy : function(){
24891         if(this.rendered){
24892             
24893             this.tb.items.each(function(item){
24894                 if(item.menu){
24895                     item.menu.removeAll();
24896                     if(item.menu.el){
24897                         item.menu.el.destroy();
24898                     }
24899                 }
24900                 item.destroy();
24901             });
24902              
24903         }
24904     },
24905     onFirstFocus: function() {
24906         this.tb.items.each(function(item){
24907            item.enable();
24908         });
24909     }
24910 });
24911
24912
24913
24914
24915 // <script type="text/javascript">
24916 /*
24917  * Based on
24918  * Ext JS Library 1.1.1
24919  * Copyright(c) 2006-2007, Ext JS, LLC.
24920  *  
24921  
24922  */
24923
24924  
24925 /**
24926  * @class Roo.form.HtmlEditor.ToolbarContext
24927  * Context Toolbar
24928  * 
24929  * Usage:
24930  *
24931  new Roo.form.HtmlEditor({
24932     ....
24933     toolbars : [
24934         new Roo.form.HtmlEditor.ToolbarStandard(),
24935         new Roo.form.HtmlEditor.ToolbarContext()
24936         })
24937     }
24938      
24939  * 
24940  * @config : {Object} disable List of elements to disable.. (not done yet.)
24941  * 
24942  * 
24943  */
24944
24945 Roo.form.HtmlEditor.ToolbarContext = function(config)
24946 {
24947     
24948     Roo.apply(this, config);
24949     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24950     // dont call parent... till later.
24951 }
24952 Roo.form.HtmlEditor.ToolbarContext.types = {
24953     'IMG' : {
24954         width : {
24955             title: "Width",
24956             width: 40
24957         },
24958         height:  {
24959             title: "Height",
24960             width: 40
24961         },
24962         align: {
24963             title: "Align",
24964             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24965             width : 80
24966             
24967         },
24968         border: {
24969             title: "Border",
24970             width: 40
24971         },
24972         alt: {
24973             title: "Alt",
24974             width: 120
24975         },
24976         src : {
24977             title: "Src",
24978             width: 220
24979         }
24980         
24981     },
24982     'A' : {
24983         name : {
24984             title: "Name",
24985             width: 50
24986         },
24987         href:  {
24988             title: "Href",
24989             width: 220
24990         } // border?
24991         
24992     },
24993     'TABLE' : {
24994         rows : {
24995             title: "Rows",
24996             width: 20
24997         },
24998         cols : {
24999             title: "Cols",
25000             width: 20
25001         },
25002         width : {
25003             title: "Width",
25004             width: 40
25005         },
25006         height : {
25007             title: "Height",
25008             width: 40
25009         },
25010         border : {
25011             title: "Border",
25012             width: 20
25013         }
25014     },
25015     'TD' : {
25016         width : {
25017             title: "Width",
25018             width: 40
25019         },
25020         height : {
25021             title: "Height",
25022             width: 40
25023         },   
25024         align: {
25025             title: "Align",
25026             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25027             width: 40
25028         },
25029         valign: {
25030             title: "Valign",
25031             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25032             width: 40
25033         },
25034         colspan: {
25035             title: "Colspan",
25036             width: 20
25037             
25038         }
25039     },
25040     'INPUT' : {
25041         name : {
25042             title: "name",
25043             width: 120
25044         },
25045         value : {
25046             title: "Value",
25047             width: 120
25048         },
25049         width : {
25050             title: "Width",
25051             width: 40
25052         }
25053     },
25054     'LABEL' : {
25055         'for' : {
25056             title: "For",
25057             width: 120
25058         }
25059     },
25060     'TEXTAREA' : {
25061           name : {
25062             title: "name",
25063             width: 120
25064         },
25065         rows : {
25066             title: "Rows",
25067             width: 20
25068         },
25069         cols : {
25070             title: "Cols",
25071             width: 20
25072         }
25073     },
25074     'SELECT' : {
25075         name : {
25076             title: "name",
25077             width: 120
25078         },
25079         selectoptions : {
25080             title: "Options",
25081             width: 200
25082         }
25083     },
25084     'BODY' : {
25085         title : {
25086             title: "title",
25087             width: 120,
25088             disabled : true
25089         }
25090     }
25091 };
25092
25093
25094
25095 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25096     
25097     tb: false,
25098     
25099     rendered: false,
25100     
25101     editor : false,
25102     /**
25103      * @cfg {Object} disable  List of toolbar elements to disable
25104          
25105      */
25106     disable : false,
25107     
25108     
25109     
25110     toolbars : false,
25111     
25112     init : function(editor)
25113     {
25114         this.editor = editor;
25115         
25116         
25117         var fid = editor.frameId;
25118         var etb = this;
25119         function btn(id, toggle, handler){
25120             var xid = fid + '-'+ id ;
25121             return {
25122                 id : xid,
25123                 cmd : id,
25124                 cls : 'x-btn-icon x-edit-'+id,
25125                 enableToggle:toggle !== false,
25126                 scope: editor, // was editor...
25127                 handler:handler||editor.relayBtnCmd,
25128                 clickEvent:'mousedown',
25129                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25130                 tabIndex:-1
25131             };
25132         }
25133         // create a new element.
25134         var wdiv = editor.wrap.createChild({
25135                 tag: 'div'
25136             }, editor.wrap.dom.firstChild.nextSibling, true);
25137         
25138         // can we do this more than once??
25139         
25140          // stop form submits
25141       
25142  
25143         // disable everything...
25144         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25145         this.toolbars = {};
25146            
25147         for (var i in  ty) {
25148             this.toolbars[i] = this.buildToolbar(ty[i],i);
25149         }
25150         this.tb = this.toolbars.BODY;
25151         this.tb.el.show();
25152         
25153          
25154         this.rendered = true;
25155         
25156         // the all the btns;
25157         editor.on('editorevent', this.updateToolbar, this);
25158         // other toolbars need to implement this..
25159         //editor.on('editmodechange', this.updateToolbar, this);
25160     },
25161     
25162     
25163     
25164     /**
25165      * Protected method that will not generally be called directly. It triggers
25166      * a toolbar update by reading the markup state of the current selection in the editor.
25167      */
25168     updateToolbar: function(){
25169
25170         if(!this.editor.activated){
25171             this.editor.onFirstFocus();
25172             return;
25173         }
25174
25175         
25176         var ans = this.editor.getAllAncestors();
25177         
25178         // pick
25179         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25180         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25181         sel = sel ? sel : this.editor.doc.body;
25182         sel = sel.tagName.length ? sel : this.editor.doc.body;
25183         var tn = sel.tagName.toUpperCase();
25184         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25185         tn = sel.tagName.toUpperCase();
25186         if (this.tb.name  == tn) {
25187             return; // no change
25188         }
25189         this.tb.el.hide();
25190         ///console.log("show: " + tn);
25191         this.tb =  this.toolbars[tn];
25192         this.tb.el.show();
25193         this.tb.fields.each(function(e) {
25194             e.setValue(sel.getAttribute(e.name));
25195         });
25196         this.tb.selectedNode = sel;
25197         
25198         
25199         Roo.menu.MenuMgr.hideAll();
25200
25201         //this.editorsyncValue();
25202     },
25203    
25204        
25205     // private
25206     onDestroy : function(){
25207         if(this.rendered){
25208             
25209             this.tb.items.each(function(item){
25210                 if(item.menu){
25211                     item.menu.removeAll();
25212                     if(item.menu.el){
25213                         item.menu.el.destroy();
25214                     }
25215                 }
25216                 item.destroy();
25217             });
25218              
25219         }
25220     },
25221     onFirstFocus: function() {
25222         // need to do this for all the toolbars..
25223         this.tb.items.each(function(item){
25224            item.enable();
25225         });
25226     },
25227     buildToolbar: function(tlist, nm)
25228     {
25229         var editor = this.editor;
25230          // create a new element.
25231         var wdiv = editor.wrap.createChild({
25232                 tag: 'div'
25233             }, editor.wrap.dom.firstChild.nextSibling, true);
25234         
25235        
25236         var tb = new Roo.Toolbar(wdiv);
25237         tb.add(nm+ ":&nbsp;");
25238         for (var i in tlist) {
25239             var item = tlist[i];
25240             tb.add(item.title + ":&nbsp;");
25241             if (item.opts) {
25242                 // fixme
25243                 
25244               
25245                 tb.addField( new Roo.form.ComboBox({
25246                     store: new Roo.data.SimpleStore({
25247                         id : 'val',
25248                         fields: ['val'],
25249                         data : item.opts // from states.js
25250                     }),
25251                     name : i,
25252                     displayField:'val',
25253                     typeAhead: false,
25254                     mode: 'local',
25255                     editable : false,
25256                     triggerAction: 'all',
25257                     emptyText:'Select',
25258                     selectOnFocus:true,
25259                     width: item.width ? item.width  : 130,
25260                     listeners : {
25261                         'select': function(c, r, i) {
25262                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25263                         }
25264                     }
25265
25266                 }));
25267                 continue;
25268                     
25269                 
25270                 
25271                 
25272                 
25273                 tb.addField( new Roo.form.TextField({
25274                     name: i,
25275                     width: 100,
25276                     //allowBlank:false,
25277                     value: ''
25278                 }));
25279                 continue;
25280             }
25281             tb.addField( new Roo.form.TextField({
25282                 name: i,
25283                 width: item.width,
25284                 //allowBlank:true,
25285                 value: '',
25286                 listeners: {
25287                     'change' : function(f, nv, ov) {
25288                         tb.selectedNode.setAttribute(f.name, nv);
25289                     }
25290                 }
25291             }));
25292              
25293         }
25294         tb.el.on('click', function(e){
25295             e.preventDefault(); // what does this do?
25296         });
25297         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25298         tb.el.hide();
25299         tb.name = nm;
25300         // dont need to disable them... as they will get hidden
25301         return tb;
25302          
25303         
25304     }
25305     
25306     
25307     
25308     
25309 });
25310
25311
25312
25313
25314
25315 /*
25316  * Based on:
25317  * Ext JS Library 1.1.1
25318  * Copyright(c) 2006-2007, Ext JS, LLC.
25319  *
25320  * Originally Released Under LGPL - original licence link has changed is not relivant.
25321  *
25322  * Fork - LGPL
25323  * <script type="text/javascript">
25324  */
25325  
25326 /**
25327  * @class Roo.form.BasicForm
25328  * @extends Roo.util.Observable
25329  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25330  * @constructor
25331  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25332  * @param {Object} config Configuration options
25333  */
25334 Roo.form.BasicForm = function(el, config){
25335     Roo.apply(this, config);
25336     /*
25337      * The Roo.form.Field items in this form.
25338      * @type MixedCollection
25339      */
25340     this.items = new Roo.util.MixedCollection(false, function(o){
25341         return o.id || (o.id = Roo.id());
25342     });
25343     this.addEvents({
25344         /**
25345          * @event beforeaction
25346          * Fires before any action is performed. Return false to cancel the action.
25347          * @param {Form} this
25348          * @param {Action} action The action to be performed
25349          */
25350         beforeaction: true,
25351         /**
25352          * @event actionfailed
25353          * Fires when an action fails.
25354          * @param {Form} this
25355          * @param {Action} action The action that failed
25356          */
25357         actionfailed : true,
25358         /**
25359          * @event actioncomplete
25360          * Fires when an action is completed.
25361          * @param {Form} this
25362          * @param {Action} action The action that completed
25363          */
25364         actioncomplete : true
25365     });
25366     if(el){
25367         this.initEl(el);
25368     }
25369     Roo.form.BasicForm.superclass.constructor.call(this);
25370 };
25371
25372 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25373     /**
25374      * @cfg {String} method
25375      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25376      */
25377     /**
25378      * @cfg {DataReader} reader
25379      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25380      * This is optional as there is built-in support for processing JSON.
25381      */
25382     /**
25383      * @cfg {DataReader} errorReader
25384      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25385      * This is completely optional as there is built-in support for processing JSON.
25386      */
25387     /**
25388      * @cfg {String} url
25389      * The URL to use for form actions if one isn't supplied in the action options.
25390      */
25391     /**
25392      * @cfg {Boolean} fileUpload
25393      * Set to true if this form is a file upload.
25394      */
25395     /**
25396      * @cfg {Object} baseParams
25397      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25398      */
25399     /**
25400      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25401      */
25402     timeout: 30,
25403
25404     // private
25405     activeAction : null,
25406
25407     /**
25408      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25409      * or setValues() data instead of when the form was first created.
25410      */
25411     trackResetOnLoad : false,
25412
25413     /**
25414      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
25415      * element by passing it or its id or mask the form itself by passing in true.
25416      * @type Mixed
25417      */
25418     waitMsgTarget : undefined,
25419
25420     // private
25421     initEl : function(el){
25422         this.el = Roo.get(el);
25423         this.id = this.el.id || Roo.id();
25424         this.el.on('submit', this.onSubmit, this);
25425         this.el.addClass('x-form');
25426     },
25427
25428     // private
25429     onSubmit : function(e){
25430         e.stopEvent();
25431     },
25432
25433     /**
25434      * Returns true if client-side validation on the form is successful.
25435      * @return Boolean
25436      */
25437     isValid : function(){
25438         var valid = true;
25439         this.items.each(function(f){
25440            if(!f.validate()){
25441                valid = false;
25442            }
25443         });
25444         return valid;
25445     },
25446
25447     /**
25448      * Returns true if any fields in this form have changed since their original load.
25449      * @return Boolean
25450      */
25451     isDirty : function(){
25452         var dirty = false;
25453         this.items.each(function(f){
25454            if(f.isDirty()){
25455                dirty = true;
25456                return false;
25457            }
25458         });
25459         return dirty;
25460     },
25461
25462     /**
25463      * Performs a predefined action (submit or load) or custom actions you define on this form.
25464      * @param {String} actionName The name of the action type
25465      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25466      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25467      * accept other config options):
25468      * <pre>
25469 Property          Type             Description
25470 ----------------  ---------------  ----------------------------------------------------------------------------------
25471 url               String           The url for the action (defaults to the form's url)
25472 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25473 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25474 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25475                                    validate the form on the client (defaults to false)
25476      * </pre>
25477      * @return {BasicForm} this
25478      */
25479     doAction : function(action, options){
25480         if(typeof action == 'string'){
25481             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25482         }
25483         if(this.fireEvent('beforeaction', this, action) !== false){
25484             this.beforeAction(action);
25485             action.run.defer(100, action);
25486         }
25487         return this;
25488     },
25489
25490     /**
25491      * Shortcut to do a submit action.
25492      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25493      * @return {BasicForm} this
25494      */
25495     submit : function(options){
25496         this.doAction('submit', options);
25497         return this;
25498     },
25499
25500     /**
25501      * Shortcut to do a load action.
25502      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25503      * @return {BasicForm} this
25504      */
25505     load : function(options){
25506         this.doAction('load', options);
25507         return this;
25508     },
25509
25510     /**
25511      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25512      * @param {Record} record The record to edit
25513      * @return {BasicForm} this
25514      */
25515     updateRecord : function(record){
25516         record.beginEdit();
25517         var fs = record.fields;
25518         fs.each(function(f){
25519             var field = this.findField(f.name);
25520             if(field){
25521                 record.set(f.name, field.getValue());
25522             }
25523         }, this);
25524         record.endEdit();
25525         return this;
25526     },
25527
25528     /**
25529      * Loads an Roo.data.Record into this form.
25530      * @param {Record} record The record to load
25531      * @return {BasicForm} this
25532      */
25533     loadRecord : function(record){
25534         this.setValues(record.data);
25535         return this;
25536     },
25537
25538     // private
25539     beforeAction : function(action){
25540         var o = action.options;
25541         if(o.waitMsg){
25542             if(this.waitMsgTarget === true){
25543                 this.el.mask(o.waitMsg, 'x-mask-loading');
25544             }else if(this.waitMsgTarget){
25545                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25546                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
25547             }else{
25548                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
25549             }
25550         }
25551     },
25552
25553     // private
25554     afterAction : function(action, success){
25555         this.activeAction = null;
25556         var o = action.options;
25557         if(o.waitMsg){
25558             if(this.waitMsgTarget === true){
25559                 this.el.unmask();
25560             }else if(this.waitMsgTarget){
25561                 this.waitMsgTarget.unmask();
25562             }else{
25563                 Roo.MessageBox.updateProgress(1);
25564                 Roo.MessageBox.hide();
25565             }
25566         }
25567         if(success){
25568             if(o.reset){
25569                 this.reset();
25570             }
25571             Roo.callback(o.success, o.scope, [this, action]);
25572             this.fireEvent('actioncomplete', this, action);
25573         }else{
25574             Roo.callback(o.failure, o.scope, [this, action]);
25575             this.fireEvent('actionfailed', this, action);
25576         }
25577     },
25578
25579     /**
25580      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25581      * @param {String} id The value to search for
25582      * @return Field
25583      */
25584     findField : function(id){
25585         var field = this.items.get(id);
25586         if(!field){
25587             this.items.each(function(f){
25588                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25589                     field = f;
25590                     return false;
25591                 }
25592             });
25593         }
25594         return field || null;
25595     },
25596
25597
25598     /**
25599      * Mark fields in this form invalid in bulk.
25600      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25601      * @return {BasicForm} this
25602      */
25603     markInvalid : function(errors){
25604         if(errors instanceof Array){
25605             for(var i = 0, len = errors.length; i < len; i++){
25606                 var fieldError = errors[i];
25607                 var f = this.findField(fieldError.id);
25608                 if(f){
25609                     f.markInvalid(fieldError.msg);
25610                 }
25611             }
25612         }else{
25613             var field, id;
25614             for(id in errors){
25615                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25616                     field.markInvalid(errors[id]);
25617                 }
25618             }
25619         }
25620         return this;
25621     },
25622
25623     /**
25624      * Set values for fields in this form in bulk.
25625      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25626      * @return {BasicForm} this
25627      */
25628     setValues : function(values){
25629         if(values instanceof Array){ // array of objects
25630             for(var i = 0, len = values.length; i < len; i++){
25631                 var v = values[i];
25632                 var f = this.findField(v.id);
25633                 if(f){
25634                     f.setValue(v.value);
25635                     if(this.trackResetOnLoad){
25636                         f.originalValue = f.getValue();
25637                     }
25638                 }
25639             }
25640         }else{ // object hash
25641             var field, id;
25642             for(id in values){
25643                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25644                     
25645                     if (field.setFromData && 
25646                         field.valueField && 
25647                         field.displayField &&
25648                         // combos' with local stores can 
25649                         // be queried via setValue()
25650                         // to set their value..
25651                         (field.store && !field.store.isLocal)
25652                         ) {
25653                         // it's a combo
25654                         var sd = { };
25655                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25656                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25657                         field.setFromData(sd);
25658                         
25659                     } else {
25660                         field.setValue(values[id]);
25661                     }
25662                     
25663                     
25664                     if(this.trackResetOnLoad){
25665                         field.originalValue = field.getValue();
25666                     }
25667                 }
25668             }
25669         }
25670         return this;
25671     },
25672
25673     /**
25674      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25675      * they are returned as an array.
25676      * @param {Boolean} asString
25677      * @return {Object}
25678      */
25679     getValues : function(asString){
25680         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25681         if(asString === true){
25682             return fs;
25683         }
25684         return Roo.urlDecode(fs);
25685     },
25686
25687     /**
25688      * Clears all invalid messages in this form.
25689      * @return {BasicForm} this
25690      */
25691     clearInvalid : function(){
25692         this.items.each(function(f){
25693            f.clearInvalid();
25694         });
25695         return this;
25696     },
25697
25698     /**
25699      * Resets this form.
25700      * @return {BasicForm} this
25701      */
25702     reset : function(){
25703         this.items.each(function(f){
25704             f.reset();
25705         });
25706         return this;
25707     },
25708
25709     /**
25710      * Add Roo.form components to this form.
25711      * @param {Field} field1
25712      * @param {Field} field2 (optional)
25713      * @param {Field} etc (optional)
25714      * @return {BasicForm} this
25715      */
25716     add : function(){
25717         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25718         return this;
25719     },
25720
25721
25722     /**
25723      * Removes a field from the items collection (does NOT remove its markup).
25724      * @param {Field} field
25725      * @return {BasicForm} this
25726      */
25727     remove : function(field){
25728         this.items.remove(field);
25729         return this;
25730     },
25731
25732     /**
25733      * Looks at the fields in this form, checks them for an id attribute,
25734      * and calls applyTo on the existing dom element with that id.
25735      * @return {BasicForm} this
25736      */
25737     render : function(){
25738         this.items.each(function(f){
25739             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25740                 f.applyTo(f.id);
25741             }
25742         });
25743         return this;
25744     },
25745
25746     /**
25747      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25748      * @param {Object} values
25749      * @return {BasicForm} this
25750      */
25751     applyToFields : function(o){
25752         this.items.each(function(f){
25753            Roo.apply(f, o);
25754         });
25755         return this;
25756     },
25757
25758     /**
25759      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25760      * @param {Object} values
25761      * @return {BasicForm} this
25762      */
25763     applyIfToFields : function(o){
25764         this.items.each(function(f){
25765            Roo.applyIf(f, o);
25766         });
25767         return this;
25768     }
25769 });
25770
25771 // back compat
25772 Roo.BasicForm = Roo.form.BasicForm;/*
25773  * Based on:
25774  * Ext JS Library 1.1.1
25775  * Copyright(c) 2006-2007, Ext JS, LLC.
25776  *
25777  * Originally Released Under LGPL - original licence link has changed is not relivant.
25778  *
25779  * Fork - LGPL
25780  * <script type="text/javascript">
25781  */
25782
25783 /**
25784  * @class Roo.form.Form
25785  * @extends Roo.form.BasicForm
25786  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25787  * @constructor
25788  * @param {Object} config Configuration options
25789  */
25790 Roo.form.Form = function(config){
25791     var xitems =  [];
25792     if (config.items) {
25793         xitems = config.items;
25794         delete config.items;
25795     }
25796     
25797     
25798     Roo.form.Form.superclass.constructor.call(this, null, config);
25799     this.url = this.url || this.action;
25800     if(!this.root){
25801         this.root = new Roo.form.Layout(Roo.applyIf({
25802             id: Roo.id()
25803         }, config));
25804     }
25805     this.active = this.root;
25806     /**
25807      * Array of all the buttons that have been added to this form via {@link addButton}
25808      * @type Array
25809      */
25810     this.buttons = [];
25811     this.allItems = [];
25812     this.addEvents({
25813         /**
25814          * @event clientvalidation
25815          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25816          * @param {Form} this
25817          * @param {Boolean} valid true if the form has passed client-side validation
25818          */
25819         clientvalidation: true,
25820         /**
25821          * @event rendered
25822          * Fires when the form is rendered
25823          * @param {Roo.form.Form} form
25824          */
25825         rendered : true
25826     });
25827     
25828     Roo.each(xitems, this.addxtype, this);
25829     
25830     
25831     
25832 };
25833
25834 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25835     /**
25836      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25837      */
25838     /**
25839      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25840      */
25841     /**
25842      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25843      */
25844     buttonAlign:'center',
25845
25846     /**
25847      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25848      */
25849     minButtonWidth:75,
25850
25851     /**
25852      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25853      * This property cascades to child containers if not set.
25854      */
25855     labelAlign:'left',
25856
25857     /**
25858      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25859      * fires a looping event with that state. This is required to bind buttons to the valid
25860      * state using the config value formBind:true on the button.
25861      */
25862     monitorValid : false,
25863
25864     /**
25865      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25866      */
25867     monitorPoll : 200,
25868
25869     /**
25870      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25871      * fields are added and the column is closed. If no fields are passed the column remains open
25872      * until end() is called.
25873      * @param {Object} config The config to pass to the column
25874      * @param {Field} field1 (optional)
25875      * @param {Field} field2 (optional)
25876      * @param {Field} etc (optional)
25877      * @return Column The column container object
25878      */
25879     column : function(c){
25880         var col = new Roo.form.Column(c);
25881         this.start(col);
25882         if(arguments.length > 1){ // duplicate code required because of Opera
25883             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25884             this.end();
25885         }
25886         return col;
25887     },
25888
25889     /**
25890      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25891      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25892      * until end() is called.
25893      * @param {Object} config The config to pass to the fieldset
25894      * @param {Field} field1 (optional)
25895      * @param {Field} field2 (optional)
25896      * @param {Field} etc (optional)
25897      * @return FieldSet The fieldset container object
25898      */
25899     fieldset : function(c){
25900         var fs = new Roo.form.FieldSet(c);
25901         this.start(fs);
25902         if(arguments.length > 1){ // duplicate code required because of Opera
25903             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25904             this.end();
25905         }
25906         return fs;
25907     },
25908
25909     /**
25910      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25911      * fields are added and the container is closed. If no fields are passed the container remains open
25912      * until end() is called.
25913      * @param {Object} config The config to pass to the Layout
25914      * @param {Field} field1 (optional)
25915      * @param {Field} field2 (optional)
25916      * @param {Field} etc (optional)
25917      * @return Layout The container object
25918      */
25919     container : function(c){
25920         var l = new Roo.form.Layout(c);
25921         this.start(l);
25922         if(arguments.length > 1){ // duplicate code required because of Opera
25923             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25924             this.end();
25925         }
25926         return l;
25927     },
25928
25929     /**
25930      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25931      * @param {Object} container A Roo.form.Layout or subclass of Layout
25932      * @return {Form} this
25933      */
25934     start : function(c){
25935         // cascade label info
25936         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25937         this.active.stack.push(c);
25938         c.ownerCt = this.active;
25939         this.active = c;
25940         return this;
25941     },
25942
25943     /**
25944      * Closes the current open container
25945      * @return {Form} this
25946      */
25947     end : function(){
25948         if(this.active == this.root){
25949             return this;
25950         }
25951         this.active = this.active.ownerCt;
25952         return this;
25953     },
25954
25955     /**
25956      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25957      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25958      * as the label of the field.
25959      * @param {Field} field1
25960      * @param {Field} field2 (optional)
25961      * @param {Field} etc. (optional)
25962      * @return {Form} this
25963      */
25964     add : function(){
25965         this.active.stack.push.apply(this.active.stack, arguments);
25966         this.allItems.push.apply(this.allItems,arguments);
25967         var r = [];
25968         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25969             if(a[i].isFormField){
25970                 r.push(a[i]);
25971             }
25972         }
25973         if(r.length > 0){
25974             Roo.form.Form.superclass.add.apply(this, r);
25975         }
25976         return this;
25977     },
25978      /**
25979      * Find any element that has been added to a form, using it's ID or name
25980      * This can include framesets, columns etc. along with regular fields..
25981      * @param {String} id - id or name to find.
25982      
25983      * @return {Element} e - or false if nothing found.
25984      */
25985     findbyId : function(id)
25986     {
25987         var ret = false;
25988         if (!id) {
25989             return ret;
25990         }
25991         Ext.each(this.allItems, function(f){
25992             if (f.id == id || f.name == id ){
25993                 ret = f;
25994                 return false;
25995             }
25996         });
25997         return ret;
25998     },
25999
26000     
26001     
26002     /**
26003      * Render this form into the passed container. This should only be called once!
26004      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26005      * @return {Form} this
26006      */
26007     render : function(ct){
26008         ct = Roo.get(ct);
26009         var o = this.autoCreate || {
26010             tag: 'form',
26011             method : this.method || 'POST',
26012             id : this.id || Roo.id()
26013         };
26014         this.initEl(ct.createChild(o));
26015
26016         this.root.render(this.el);
26017
26018         this.items.each(function(f){
26019             f.render('x-form-el-'+f.id);
26020         });
26021
26022         if(this.buttons.length > 0){
26023             // tables are required to maintain order and for correct IE layout
26024             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26025                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26026                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26027             }}, null, true);
26028             var tr = tb.getElementsByTagName('tr')[0];
26029             for(var i = 0, len = this.buttons.length; i < len; i++) {
26030                 var b = this.buttons[i];
26031                 var td = document.createElement('td');
26032                 td.className = 'x-form-btn-td';
26033                 b.render(tr.appendChild(td));
26034             }
26035         }
26036         if(this.monitorValid){ // initialize after render
26037             this.startMonitoring();
26038         }
26039         this.fireEvent('rendered', this);
26040         return this;
26041     },
26042
26043     /**
26044      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26045      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26046      * object or a valid Roo.DomHelper element config
26047      * @param {Function} handler The function called when the button is clicked
26048      * @param {Object} scope (optional) The scope of the handler function
26049      * @return {Roo.Button}
26050      */
26051     addButton : function(config, handler, scope){
26052         var bc = {
26053             handler: handler,
26054             scope: scope,
26055             minWidth: this.minButtonWidth,
26056             hideParent:true
26057         };
26058         if(typeof config == "string"){
26059             bc.text = config;
26060         }else{
26061             Roo.apply(bc, config);
26062         }
26063         var btn = new Roo.Button(null, bc);
26064         this.buttons.push(btn);
26065         return btn;
26066     },
26067
26068      /**
26069      * Adds a series of form elements (using the xtype property as the factory method.
26070      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26071      * @param {Object} config 
26072      */
26073     
26074     addxtype : function()
26075     {
26076         var ar = Array.prototype.slice.call(arguments, 0);
26077         var ret = false;
26078         for(var i = 0; i < ar.length; i++) {
26079             if (!ar[i]) {
26080                 continue; // skip -- if this happends something invalid got sent, we 
26081                 // should ignore it, as basically that interface element will not show up
26082                 // and that should be pretty obvious!!
26083             }
26084             
26085             if (Roo.form[ar[i].xtype]) {
26086                 ar[i].form = this;
26087                 var fe = Roo.factory(ar[i], Roo.form);
26088                 if (!ret) {
26089                     ret = fe;
26090                 }
26091                 fe.form = this;
26092                 if (fe.store) {
26093                     fe.store.form = this;
26094                 }
26095                 if (fe.isLayout) {  
26096                          
26097                     this.start(fe);
26098                     this.allItems.push(fe);
26099                     if (fe.items && fe.addxtype) {
26100                         fe.addxtype.apply(fe, fe.items);
26101                         delete fe.items;
26102                     }
26103                      this.end();
26104                     continue;
26105                 }
26106                 
26107                 
26108                  
26109                 this.add(fe);
26110               //  console.log('adding ' + ar[i].xtype);
26111             }
26112             if (ar[i].xtype == 'Button') {  
26113                 //console.log('adding button');
26114                 //console.log(ar[i]);
26115                 this.addButton(ar[i]);
26116                 this.allItems.push(fe);
26117                 continue;
26118             }
26119             
26120             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26121                 alert('end is not supported on xtype any more, use items');
26122             //    this.end();
26123             //    //console.log('adding end');
26124             }
26125             
26126         }
26127         return ret;
26128     },
26129     
26130     /**
26131      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26132      * option "monitorValid"
26133      */
26134     startMonitoring : function(){
26135         if(!this.bound){
26136             this.bound = true;
26137             Roo.TaskMgr.start({
26138                 run : this.bindHandler,
26139                 interval : this.monitorPoll || 200,
26140                 scope: this
26141             });
26142         }
26143     },
26144
26145     /**
26146      * Stops monitoring of the valid state of this form
26147      */
26148     stopMonitoring : function(){
26149         this.bound = false;
26150     },
26151
26152     // private
26153     bindHandler : function(){
26154         if(!this.bound){
26155             return false; // stops binding
26156         }
26157         var valid = true;
26158         this.items.each(function(f){
26159             if(!f.isValid(true)){
26160                 valid = false;
26161                 return false;
26162             }
26163         });
26164         for(var i = 0, len = this.buttons.length; i < len; i++){
26165             var btn = this.buttons[i];
26166             if(btn.formBind === true && btn.disabled === valid){
26167                 btn.setDisabled(!valid);
26168             }
26169         }
26170         this.fireEvent('clientvalidation', this, valid);
26171     }
26172     
26173     
26174     
26175     
26176     
26177     
26178     
26179     
26180 });
26181
26182
26183 // back compat
26184 Roo.Form = Roo.form.Form;
26185 /*
26186  * Based on:
26187  * Ext JS Library 1.1.1
26188  * Copyright(c) 2006-2007, Ext JS, LLC.
26189  *
26190  * Originally Released Under LGPL - original licence link has changed is not relivant.
26191  *
26192  * Fork - LGPL
26193  * <script type="text/javascript">
26194  */
26195  
26196  /**
26197  * @class Roo.form.Action
26198  * Internal Class used to handle form actions
26199  * @constructor
26200  * @param {Roo.form.BasicForm} el The form element or its id
26201  * @param {Object} config Configuration options
26202  */
26203  
26204  
26205 // define the action interface
26206 Roo.form.Action = function(form, options){
26207     this.form = form;
26208     this.options = options || {};
26209 };
26210 /**
26211  * Client Validation Failed
26212  * @const 
26213  */
26214 Roo.form.Action.CLIENT_INVALID = 'client';
26215 /**
26216  * Server Validation Failed
26217  * @const 
26218  */
26219  Roo.form.Action.SERVER_INVALID = 'server';
26220  /**
26221  * Connect to Server Failed
26222  * @const 
26223  */
26224 Roo.form.Action.CONNECT_FAILURE = 'connect';
26225 /**
26226  * Reading Data from Server Failed
26227  * @const 
26228  */
26229 Roo.form.Action.LOAD_FAILURE = 'load';
26230
26231 Roo.form.Action.prototype = {
26232     type : 'default',
26233     failureType : undefined,
26234     response : undefined,
26235     result : undefined,
26236
26237     // interface method
26238     run : function(options){
26239
26240     },
26241
26242     // interface method
26243     success : function(response){
26244
26245     },
26246
26247     // interface method
26248     handleResponse : function(response){
26249
26250     },
26251
26252     // default connection failure
26253     failure : function(response){
26254         this.response = response;
26255         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26256         this.form.afterAction(this, false);
26257     },
26258
26259     processResponse : function(response){
26260         this.response = response;
26261         if(!response.responseText){
26262             return true;
26263         }
26264         this.result = this.handleResponse(response);
26265         return this.result;
26266     },
26267
26268     // utility functions used internally
26269     getUrl : function(appendParams){
26270         var url = this.options.url || this.form.url || this.form.el.dom.action;
26271         if(appendParams){
26272             var p = this.getParams();
26273             if(p){
26274                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26275             }
26276         }
26277         return url;
26278     },
26279
26280     getMethod : function(){
26281         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26282     },
26283
26284     getParams : function(){
26285         var bp = this.form.baseParams;
26286         var p = this.options.params;
26287         if(p){
26288             if(typeof p == "object"){
26289                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26290             }else if(typeof p == 'string' && bp){
26291                 p += '&' + Roo.urlEncode(bp);
26292             }
26293         }else if(bp){
26294             p = Roo.urlEncode(bp);
26295         }
26296         return p;
26297     },
26298
26299     createCallback : function(){
26300         return {
26301             success: this.success,
26302             failure: this.failure,
26303             scope: this,
26304             timeout: (this.form.timeout*1000),
26305             upload: this.form.fileUpload ? this.success : undefined
26306         };
26307     }
26308 };
26309
26310 Roo.form.Action.Submit = function(form, options){
26311     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26312 };
26313
26314 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26315     type : 'submit',
26316
26317     run : function(){
26318         var o = this.options;
26319         var method = this.getMethod();
26320         var isPost = method == 'POST';
26321         if(o.clientValidation === false || this.form.isValid()){
26322             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26323                 form:this.form.el.dom,
26324                 url:this.getUrl(!isPost),
26325                 method: method,
26326                 params:isPost ? this.getParams() : null,
26327                 isUpload: this.form.fileUpload
26328             }));
26329
26330         }else if (o.clientValidation !== false){ // client validation failed
26331             this.failureType = Roo.form.Action.CLIENT_INVALID;
26332             this.form.afterAction(this, false);
26333         }
26334     },
26335
26336     success : function(response){
26337         var result = this.processResponse(response);
26338         if(result === true || result.success){
26339             this.form.afterAction(this, true);
26340             return;
26341         }
26342         if(result.errors){
26343             this.form.markInvalid(result.errors);
26344             this.failureType = Roo.form.Action.SERVER_INVALID;
26345         }
26346         this.form.afterAction(this, false);
26347     },
26348
26349     handleResponse : function(response){
26350         if(this.form.errorReader){
26351             var rs = this.form.errorReader.read(response);
26352             var errors = [];
26353             if(rs.records){
26354                 for(var i = 0, len = rs.records.length; i < len; i++) {
26355                     var r = rs.records[i];
26356                     errors[i] = r.data;
26357                 }
26358             }
26359             if(errors.length < 1){
26360                 errors = null;
26361             }
26362             return {
26363                 success : rs.success,
26364                 errors : errors
26365             };
26366         }
26367         var ret = false;
26368         try {
26369             ret = Roo.decode(response.responseText);
26370         } catch (e) {
26371             ret = {
26372                 success: false,
26373                 errorMsg: "Failed to read server message: " + response.responseText,
26374                 errors : []
26375             };
26376         }
26377         return ret;
26378         
26379     }
26380 });
26381
26382
26383 Roo.form.Action.Load = function(form, options){
26384     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26385     this.reader = this.form.reader;
26386 };
26387
26388 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26389     type : 'load',
26390
26391     run : function(){
26392         Roo.Ajax.request(Roo.apply(
26393                 this.createCallback(), {
26394                     method:this.getMethod(),
26395                     url:this.getUrl(false),
26396                     params:this.getParams()
26397         }));
26398     },
26399
26400     success : function(response){
26401         var result = this.processResponse(response);
26402         if(result === true || !result.success || !result.data){
26403             this.failureType = Roo.form.Action.LOAD_FAILURE;
26404             this.form.afterAction(this, false);
26405             return;
26406         }
26407         this.form.clearInvalid();
26408         this.form.setValues(result.data);
26409         this.form.afterAction(this, true);
26410     },
26411
26412     handleResponse : function(response){
26413         if(this.form.reader){
26414             var rs = this.form.reader.read(response);
26415             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26416             return {
26417                 success : rs.success,
26418                 data : data
26419             };
26420         }
26421         return Roo.decode(response.responseText);
26422     }
26423 });
26424
26425 Roo.form.Action.ACTION_TYPES = {
26426     'load' : Roo.form.Action.Load,
26427     'submit' : Roo.form.Action.Submit
26428 };/*
26429  * Based on:
26430  * Ext JS Library 1.1.1
26431  * Copyright(c) 2006-2007, Ext JS, LLC.
26432  *
26433  * Originally Released Under LGPL - original licence link has changed is not relivant.
26434  *
26435  * Fork - LGPL
26436  * <script type="text/javascript">
26437  */
26438  
26439 /**
26440  * @class Roo.form.Layout
26441  * @extends Roo.Component
26442  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26443  * @constructor
26444  * @param {Object} config Configuration options
26445  */
26446 Roo.form.Layout = function(config){
26447     var xitems = [];
26448     if (config.items) {
26449         xitems = config.items;
26450         delete config.items;
26451     }
26452     Roo.form.Layout.superclass.constructor.call(this, config);
26453     this.stack = [];
26454     Roo.each(xitems, this.addxtype, this);
26455      
26456 };
26457
26458 Roo.extend(Roo.form.Layout, Roo.Component, {
26459     /**
26460      * @cfg {String/Object} autoCreate
26461      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26462      */
26463     /**
26464      * @cfg {String/Object/Function} style
26465      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26466      * a function which returns such a specification.
26467      */
26468     /**
26469      * @cfg {String} labelAlign
26470      * Valid values are "left," "top" and "right" (defaults to "left")
26471      */
26472     /**
26473      * @cfg {Number} labelWidth
26474      * Fixed width in pixels of all field labels (defaults to undefined)
26475      */
26476     /**
26477      * @cfg {Boolean} clear
26478      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26479      */
26480     clear : true,
26481     /**
26482      * @cfg {String} labelSeparator
26483      * The separator to use after field labels (defaults to ':')
26484      */
26485     labelSeparator : ':',
26486     /**
26487      * @cfg {Boolean} hideLabels
26488      * True to suppress the display of field labels in this layout (defaults to false)
26489      */
26490     hideLabels : false,
26491
26492     // private
26493     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26494     
26495     isLayout : true,
26496     
26497     // private
26498     onRender : function(ct, position){
26499         if(this.el){ // from markup
26500             this.el = Roo.get(this.el);
26501         }else {  // generate
26502             var cfg = this.getAutoCreate();
26503             this.el = ct.createChild(cfg, position);
26504         }
26505         if(this.style){
26506             this.el.applyStyles(this.style);
26507         }
26508         if(this.labelAlign){
26509             this.el.addClass('x-form-label-'+this.labelAlign);
26510         }
26511         if(this.hideLabels){
26512             this.labelStyle = "display:none";
26513             this.elementStyle = "padding-left:0;";
26514         }else{
26515             if(typeof this.labelWidth == 'number'){
26516                 this.labelStyle = "width:"+this.labelWidth+"px;";
26517                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26518             }
26519             if(this.labelAlign == 'top'){
26520                 this.labelStyle = "width:auto;";
26521                 this.elementStyle = "padding-left:0;";
26522             }
26523         }
26524         var stack = this.stack;
26525         var slen = stack.length;
26526         if(slen > 0){
26527             if(!this.fieldTpl){
26528                 var t = new Roo.Template(
26529                     '<div class="x-form-item {5}">',
26530                         '<label for="{0}" style="{2}">{1}{4}</label>',
26531                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26532                         '</div>',
26533                     '</div><div class="x-form-clear-left"></div>'
26534                 );
26535                 t.disableFormats = true;
26536                 t.compile();
26537                 Roo.form.Layout.prototype.fieldTpl = t;
26538             }
26539             for(var i = 0; i < slen; i++) {
26540                 if(stack[i].isFormField){
26541                     this.renderField(stack[i]);
26542                 }else{
26543                     this.renderComponent(stack[i]);
26544                 }
26545             }
26546         }
26547         if(this.clear){
26548             this.el.createChild({cls:'x-form-clear'});
26549         }
26550     },
26551
26552     // private
26553     renderField : function(f){
26554         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26555                f.id, //0
26556                f.fieldLabel, //1
26557                f.labelStyle||this.labelStyle||'', //2
26558                this.elementStyle||'', //3
26559                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26560                f.itemCls||this.itemCls||''  //5
26561        ], true).getPrevSibling());
26562     },
26563
26564     // private
26565     renderComponent : function(c){
26566         c.render(c.isLayout ? this.el : this.el.createChild());    
26567     },
26568     /**
26569      * Adds a object form elements (using the xtype property as the factory method.)
26570      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26571      * @param {Object} config 
26572      */
26573     addxtype : function(o)
26574     {
26575         // create the lement.
26576         o.form = this.form;
26577         var fe = Roo.factory(o, Roo.form);
26578         this.form.allItems.push(fe);
26579         this.stack.push(fe);
26580         
26581         if (fe.isFormField) {
26582             this.form.items.add(fe);
26583         }
26584          
26585         return fe;
26586     }
26587 });
26588
26589 /**
26590  * @class Roo.form.Column
26591  * @extends Roo.form.Layout
26592  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26593  * @constructor
26594  * @param {Object} config Configuration options
26595  */
26596 Roo.form.Column = function(config){
26597     Roo.form.Column.superclass.constructor.call(this, config);
26598 };
26599
26600 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26601     /**
26602      * @cfg {Number/String} width
26603      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26604      */
26605     /**
26606      * @cfg {String/Object} autoCreate
26607      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26608      */
26609
26610     // private
26611     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26612
26613     // private
26614     onRender : function(ct, position){
26615         Roo.form.Column.superclass.onRender.call(this, ct, position);
26616         if(this.width){
26617             this.el.setWidth(this.width);
26618         }
26619     }
26620 });
26621
26622
26623 /**
26624  * @class Roo.form.Row
26625  * @extends Roo.form.Layout
26626  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26627  * @constructor
26628  * @param {Object} config Configuration options
26629  */
26630
26631  
26632 Roo.form.Row = function(config){
26633     Roo.form.Row.superclass.constructor.call(this, config);
26634 };
26635  
26636 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26637       /**
26638      * @cfg {Number/String} width
26639      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26640      */
26641     /**
26642      * @cfg {Number/String} height
26643      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26644      */
26645     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26646     
26647     padWidth : 20,
26648     // private
26649     onRender : function(ct, position){
26650         //console.log('row render');
26651         if(!this.rowTpl){
26652             var t = new Roo.Template(
26653                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26654                     '<label for="{0}" style="{2}">{1}{4}</label>',
26655                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26656                     '</div>',
26657                 '</div>'
26658             );
26659             t.disableFormats = true;
26660             t.compile();
26661             Roo.form.Layout.prototype.rowTpl = t;
26662         }
26663         this.fieldTpl = this.rowTpl;
26664         
26665         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26666         var labelWidth = 100;
26667         
26668         if ((this.labelAlign != 'top')) {
26669             if (typeof this.labelWidth == 'number') {
26670                 labelWidth = this.labelWidth
26671             }
26672             this.padWidth =  20 + labelWidth;
26673             
26674         }
26675         
26676         Roo.form.Column.superclass.onRender.call(this, ct, position);
26677         if(this.width){
26678             this.el.setWidth(this.width);
26679         }
26680         if(this.height){
26681             this.el.setHeight(this.height);
26682         }
26683     },
26684     
26685     // private
26686     renderField : function(f){
26687         f.fieldEl = this.fieldTpl.append(this.el, [
26688                f.id, f.fieldLabel,
26689                f.labelStyle||this.labelStyle||'',
26690                this.elementStyle||'',
26691                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26692                f.itemCls||this.itemCls||'',
26693                f.width ? f.width + this.padWidth : 160 + this.padWidth
26694        ],true);
26695     }
26696 });
26697  
26698
26699 /**
26700  * @class Roo.form.FieldSet
26701  * @extends Roo.form.Layout
26702  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26703  * @constructor
26704  * @param {Object} config Configuration options
26705  */
26706 Roo.form.FieldSet = function(config){
26707     Roo.form.FieldSet.superclass.constructor.call(this, config);
26708 };
26709
26710 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26711     /**
26712      * @cfg {String} legend
26713      * The text to display as the legend for the FieldSet (defaults to '')
26714      */
26715     /**
26716      * @cfg {String/Object} autoCreate
26717      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26718      */
26719
26720     // private
26721     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26722
26723     // private
26724     onRender : function(ct, position){
26725         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26726         if(this.legend){
26727             this.setLegend(this.legend);
26728         }
26729     },
26730
26731     // private
26732     setLegend : function(text){
26733         if(this.rendered){
26734             this.el.child('legend').update(text);
26735         }
26736     }
26737 });/*
26738  * Based on:
26739  * Ext JS Library 1.1.1
26740  * Copyright(c) 2006-2007, Ext JS, LLC.
26741  *
26742  * Originally Released Under LGPL - original licence link has changed is not relivant.
26743  *
26744  * Fork - LGPL
26745  * <script type="text/javascript">
26746  */
26747 /**
26748  * @class Roo.form.VTypes
26749  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26750  * @singleton
26751  */
26752 Roo.form.VTypes = function(){
26753     // closure these in so they are only created once.
26754     var alpha = /^[a-zA-Z_]+$/;
26755     var alphanum = /^[a-zA-Z0-9_]+$/;
26756     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
26757     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26758
26759     // All these messages and functions are configurable
26760     return {
26761         /**
26762          * The function used to validate email addresses
26763          * @param {String} value The email address
26764          */
26765         'email' : function(v){
26766             return email.test(v);
26767         },
26768         /**
26769          * The error text to display when the email validation function returns false
26770          * @type String
26771          */
26772         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26773         /**
26774          * The keystroke filter mask to be applied on email input
26775          * @type RegExp
26776          */
26777         'emailMask' : /[a-z0-9_\.\-@]/i,
26778
26779         /**
26780          * The function used to validate URLs
26781          * @param {String} value The URL
26782          */
26783         'url' : function(v){
26784             return url.test(v);
26785         },
26786         /**
26787          * The error text to display when the url validation function returns false
26788          * @type String
26789          */
26790         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26791         
26792         /**
26793          * The function used to validate alpha values
26794          * @param {String} value The value
26795          */
26796         'alpha' : function(v){
26797             return alpha.test(v);
26798         },
26799         /**
26800          * The error text to display when the alpha validation function returns false
26801          * @type String
26802          */
26803         'alphaText' : 'This field should only contain letters and _',
26804         /**
26805          * The keystroke filter mask to be applied on alpha input
26806          * @type RegExp
26807          */
26808         'alphaMask' : /[a-z_]/i,
26809
26810         /**
26811          * The function used to validate alphanumeric values
26812          * @param {String} value The value
26813          */
26814         'alphanum' : function(v){
26815             return alphanum.test(v);
26816         },
26817         /**
26818          * The error text to display when the alphanumeric validation function returns false
26819          * @type String
26820          */
26821         'alphanumText' : 'This field should only contain letters, numbers and _',
26822         /**
26823          * The keystroke filter mask to be applied on alphanumeric input
26824          * @type RegExp
26825          */
26826         'alphanumMask' : /[a-z0-9_]/i
26827     };
26828 }();//<script type="text/javascript">
26829
26830 /**
26831  * @class Roo.form.FCKeditor
26832  * @extends Roo.form.TextArea
26833  * Wrapper around the FCKEditor http://www.fckeditor.net
26834  * @constructor
26835  * Creates a new FCKeditor
26836  * @param {Object} config Configuration options
26837  */
26838 Roo.form.FCKeditor = function(config){
26839     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26840     this.addEvents({
26841          /**
26842          * @event editorinit
26843          * Fired when the editor is initialized - you can add extra handlers here..
26844          * @param {FCKeditor} this
26845          * @param {Object} the FCK object.
26846          */
26847         editorinit : true
26848     });
26849     
26850     
26851 };
26852 Roo.form.FCKeditor.editors = { };
26853 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26854 {
26855     //defaultAutoCreate : {
26856     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26857     //},
26858     // private
26859     /**
26860      * @cfg {Object} fck options - see fck manual for details.
26861      */
26862     fckconfig : false,
26863     
26864     /**
26865      * @cfg {Object} fck toolbar set (Basic or Default)
26866      */
26867     toolbarSet : 'Basic',
26868     /**
26869      * @cfg {Object} fck BasePath
26870      */ 
26871     basePath : '/fckeditor/',
26872     
26873     
26874     frame : false,
26875     
26876     value : '',
26877     
26878    
26879     onRender : function(ct, position)
26880     {
26881         if(!this.el){
26882             this.defaultAutoCreate = {
26883                 tag: "textarea",
26884                 style:"width:300px;height:60px;",
26885                 autocomplete: "off"
26886             };
26887         }
26888         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26889         /*
26890         if(this.grow){
26891             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26892             if(this.preventScrollbars){
26893                 this.el.setStyle("overflow", "hidden");
26894             }
26895             this.el.setHeight(this.growMin);
26896         }
26897         */
26898         //console.log('onrender' + this.getId() );
26899         Roo.form.FCKeditor.editors[this.getId()] = this;
26900          
26901
26902         this.replaceTextarea() ;
26903         
26904     },
26905     
26906     getEditor : function() {
26907         return this.fckEditor;
26908     },
26909     /**
26910      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26911      * @param {Mixed} value The value to set
26912      */
26913     
26914     
26915     setValue : function(value)
26916     {
26917         //console.log('setValue: ' + value);
26918         
26919         if(typeof(value) == 'undefined') { // not sure why this is happending...
26920             return;
26921         }
26922         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26923         
26924         //if(!this.el || !this.getEditor()) {
26925         //    this.value = value;
26926             //this.setValue.defer(100,this,[value]);    
26927         //    return;
26928         //} 
26929         
26930         if(!this.getEditor()) {
26931             return;
26932         }
26933         
26934         this.getEditor().SetData(value);
26935         
26936         //
26937
26938     },
26939
26940     /**
26941      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26942      * @return {Mixed} value The field value
26943      */
26944     getValue : function()
26945     {
26946         
26947         if (this.frame && this.frame.dom.style.display == 'none') {
26948             return Roo.form.FCKeditor.superclass.getValue.call(this);
26949         }
26950         
26951         if(!this.el || !this.getEditor()) {
26952            
26953            // this.getValue.defer(100,this); 
26954             return this.value;
26955         }
26956        
26957         
26958         var value=this.getEditor().GetData();
26959         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26960         return Roo.form.FCKeditor.superclass.getValue.call(this);
26961         
26962
26963     },
26964
26965     /**
26966      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26967      * @return {Mixed} value The field value
26968      */
26969     getRawValue : function()
26970     {
26971         if (this.frame && this.frame.dom.style.display == 'none') {
26972             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26973         }
26974         
26975         if(!this.el || !this.getEditor()) {
26976             //this.getRawValue.defer(100,this); 
26977             return this.value;
26978             return;
26979         }
26980         
26981         
26982         
26983         var value=this.getEditor().GetData();
26984         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26985         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26986          
26987     },
26988     
26989     setSize : function(w,h) {
26990         
26991         
26992         
26993         //if (this.frame && this.frame.dom.style.display == 'none') {
26994         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26995         //    return;
26996         //}
26997         //if(!this.el || !this.getEditor()) {
26998         //    this.setSize.defer(100,this, [w,h]); 
26999         //    return;
27000         //}
27001         
27002         
27003         
27004         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27005         
27006         this.frame.dom.setAttribute('width', w);
27007         this.frame.dom.setAttribute('height', h);
27008         this.frame.setSize(w,h);
27009         
27010     },
27011     
27012     toggleSourceEdit : function(value) {
27013         
27014       
27015          
27016         this.el.dom.style.display = value ? '' : 'none';
27017         this.frame.dom.style.display = value ?  'none' : '';
27018         
27019     },
27020     
27021     
27022     focus: function(tag)
27023     {
27024         if (this.frame.dom.style.display == 'none') {
27025             return Roo.form.FCKeditor.superclass.focus.call(this);
27026         }
27027         if(!this.el || !this.getEditor()) {
27028             this.focus.defer(100,this, [tag]); 
27029             return;
27030         }
27031         
27032         
27033         
27034         
27035         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27036         this.getEditor().Focus();
27037         if (tgs.length) {
27038             if (!this.getEditor().Selection.GetSelection()) {
27039                 this.focus.defer(100,this, [tag]); 
27040                 return;
27041             }
27042             
27043             
27044             var r = this.getEditor().EditorDocument.createRange();
27045             r.setStart(tgs[0],0);
27046             r.setEnd(tgs[0],0);
27047             this.getEditor().Selection.GetSelection().removeAllRanges();
27048             this.getEditor().Selection.GetSelection().addRange(r);
27049             this.getEditor().Focus();
27050         }
27051         
27052     },
27053     
27054     
27055     
27056     replaceTextarea : function()
27057     {
27058         if ( document.getElementById( this.getId() + '___Frame' ) )
27059             return ;
27060         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27061         //{
27062             // We must check the elements firstly using the Id and then the name.
27063         var oTextarea = document.getElementById( this.getId() );
27064         
27065         var colElementsByName = document.getElementsByName( this.getId() ) ;
27066          
27067         oTextarea.style.display = 'none' ;
27068
27069         if ( oTextarea.tabIndex ) {            
27070             this.TabIndex = oTextarea.tabIndex ;
27071         }
27072         
27073         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27074         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27075         this.frame = Roo.get(this.getId() + '___Frame')
27076     },
27077     
27078     _getConfigHtml : function()
27079     {
27080         var sConfig = '' ;
27081
27082         for ( var o in this.fckconfig ) {
27083             sConfig += sConfig.length > 0  ? '&amp;' : '';
27084             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27085         }
27086
27087         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27088     },
27089     
27090     
27091     _getIFrameHtml : function()
27092     {
27093         var sFile = 'fckeditor.html' ;
27094         /* no idea what this is about..
27095         try
27096         {
27097             if ( (/fcksource=true/i).test( window.top.location.search ) )
27098                 sFile = 'fckeditor.original.html' ;
27099         }
27100         catch (e) { 
27101         */
27102
27103         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27104         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27105         
27106         
27107         var html = '<iframe id="' + this.getId() +
27108             '___Frame" src="' + sLink +
27109             '" width="' + this.width +
27110             '" height="' + this.height + '"' +
27111             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27112             ' frameborder="0" scrolling="no"></iframe>' ;
27113
27114         return html ;
27115     },
27116     
27117     _insertHtmlBefore : function( html, element )
27118     {
27119         if ( element.insertAdjacentHTML )       {
27120             // IE
27121             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27122         } else { // Gecko
27123             var oRange = document.createRange() ;
27124             oRange.setStartBefore( element ) ;
27125             var oFragment = oRange.createContextualFragment( html );
27126             element.parentNode.insertBefore( oFragment, element ) ;
27127         }
27128     }
27129     
27130     
27131   
27132     
27133     
27134     
27135     
27136
27137 });
27138
27139 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27140
27141 function FCKeditor_OnComplete(editorInstance){
27142     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27143     f.fckEditor = editorInstance;
27144     //console.log("loaded");
27145     f.fireEvent('editorinit', f, editorInstance);
27146
27147   
27148
27149  
27150
27151
27152
27153
27154
27155
27156
27157
27158
27159
27160
27161
27162
27163
27164
27165 //<script type="text/javascript">
27166 /**
27167  * @class Roo.form.GridField
27168  * @extends Roo.form.Field
27169  * Embed a grid (or editable grid into a form)
27170  * STATUS ALPHA
27171  * @constructor
27172  * Creates a new GridField
27173  * @param {Object} config Configuration options
27174  */
27175 Roo.form.GridField = function(config){
27176     Roo.form.GridField.superclass.constructor.call(this, config);
27177      
27178 };
27179
27180 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27181     /**
27182      * @cfg {Number} width  - used to restrict width of grid..
27183      */
27184     width : 100,
27185     /**
27186      * @cfg {Number} height - used to restrict height of grid..
27187      */
27188     height : 50,
27189      /**
27190      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
27191      */
27192     xgrid : false, 
27193     /**
27194      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27195      * {tag: "input", type: "checkbox", autocomplete: "off"})
27196      */
27197    // defaultAutoCreate : { tag: 'div' },
27198     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27199     /**
27200      * @cfg {String} addTitle Text to include for adding a title.
27201      */
27202     addTitle : false,
27203     //
27204     onResize : function(){
27205         Roo.form.Field.superclass.onResize.apply(this, arguments);
27206     },
27207
27208     initEvents : function(){
27209         // Roo.form.Checkbox.superclass.initEvents.call(this);
27210         // has no events...
27211        
27212     },
27213
27214
27215     getResizeEl : function(){
27216         return this.wrap;
27217     },
27218
27219     getPositionEl : function(){
27220         return this.wrap;
27221     },
27222
27223     // private
27224     onRender : function(ct, position){
27225         
27226         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27227         var style = this.style;
27228         delete this.style;
27229         
27230         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
27231         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27232         this.viewEl = this.wrap.createChild({ tag: 'div' });
27233         if (style) {
27234             this.viewEl.applyStyles(style);
27235         }
27236         if (this.width) {
27237             this.viewEl.setWidth(this.width);
27238         }
27239         if (this.height) {
27240             this.viewEl.setHeight(this.height);
27241         }
27242         //if(this.inputValue !== undefined){
27243         //this.setValue(this.value);
27244         
27245         
27246         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27247         
27248         
27249         this.grid.render();
27250         this.grid.getDataSource().on('remove', this.refreshValue, this);
27251         this.grid.getDataSource().on('update', this.refreshValue, this);
27252         this.grid.on('afteredit', this.refreshValue, this);
27253  
27254     },
27255      
27256     
27257     /**
27258      * Sets the value of the item. 
27259      * @param {String} either an object  or a string..
27260      */
27261     setValue : function(v){
27262         //this.value = v;
27263         v = v || []; // empty set..
27264         // this does not seem smart - it really only affects memoryproxy grids..
27265         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27266             var ds = this.grid.getDataSource();
27267             // assumes a json reader..
27268             var data = {}
27269             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27270             ds.loadData( data);
27271         }
27272         Roo.form.GridField.superclass.setValue.call(this, v);
27273         this.refreshValue();
27274         // should load data in the grid really....
27275     },
27276     
27277     // private
27278     refreshValue: function() {
27279          var val = [];
27280         this.grid.getDataSource().each(function(r) {
27281             val.push(r.data);
27282         });
27283         this.el.dom.value = Roo.encode(val);
27284     }
27285     
27286      
27287     
27288     
27289 });//<script type="text/javasscript">
27290  
27291
27292 /**
27293  * @class Roo.DDView
27294  * A DnD enabled version of Roo.View.
27295  * @param {Element/String} container The Element in which to create the View.
27296  * @param {String} tpl The template string used to create the markup for each element of the View
27297  * @param {Object} config The configuration properties. These include all the config options of
27298  * {@link Roo.View} plus some specific to this class.<br>
27299  * <p>
27300  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
27301  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
27302  * <p>
27303  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
27304 .x-view-drag-insert-above {
27305         border-top:1px dotted #3366cc;
27306 }
27307 .x-view-drag-insert-below {
27308         border-bottom:1px dotted #3366cc;
27309 }
27310 </code></pre>
27311  * 
27312  */
27313  
27314 Roo.DDView = function(container, tpl, config) {
27315     Roo.DDView.superclass.constructor.apply(this, arguments);
27316     this.getEl().setStyle("outline", "0px none");
27317     this.getEl().unselectable();
27318     if (this.dragGroup) {
27319                 this.setDraggable(this.dragGroup.split(","));
27320     }
27321     if (this.dropGroup) {
27322                 this.setDroppable(this.dropGroup.split(","));
27323     }
27324     if (this.deletable) {
27325         this.setDeletable();
27326     }
27327     this.isDirtyFlag = false;
27328         this.addEvents({
27329                 "drop" : true
27330         });
27331 };
27332
27333 Roo.extend(Roo.DDView, Roo.View, {
27334 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
27335 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
27336 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
27337 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
27338
27339         isFormField: true,
27340
27341         reset: Roo.emptyFn,
27342         
27343         clearInvalid: Roo.form.Field.prototype.clearInvalid,
27344
27345         validate: function() {
27346                 return true;
27347         },
27348         
27349         destroy: function() {
27350                 this.purgeListeners();
27351                 this.getEl.removeAllListeners();
27352                 this.getEl().remove();
27353                 if (this.dragZone) {
27354                         if (this.dragZone.destroy) {
27355                                 this.dragZone.destroy();
27356                         }
27357                 }
27358                 if (this.dropZone) {
27359                         if (this.dropZone.destroy) {
27360                                 this.dropZone.destroy();
27361                         }
27362                 }
27363         },
27364
27365 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
27366         getName: function() {
27367                 return this.name;
27368         },
27369
27370 /**     Loads the View from a JSON string representing the Records to put into the Store. */
27371         setValue: function(v) {
27372                 if (!this.store) {
27373                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
27374                 }
27375                 var data = {};
27376                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
27377                 this.store.proxy = new Roo.data.MemoryProxy(data);
27378                 this.store.load();
27379         },
27380
27381 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
27382         getValue: function() {
27383                 var result = '(';
27384                 this.store.each(function(rec) {
27385                         result += rec.id + ',';
27386                 });
27387                 return result.substr(0, result.length - 1) + ')';
27388         },
27389         
27390         getIds: function() {
27391                 var i = 0, result = new Array(this.store.getCount());
27392                 this.store.each(function(rec) {
27393                         result[i++] = rec.id;
27394                 });
27395                 return result;
27396         },
27397         
27398         isDirty: function() {
27399                 return this.isDirtyFlag;
27400         },
27401
27402 /**
27403  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
27404  *      whole Element becomes the target, and this causes the drop gesture to append.
27405  */
27406     getTargetFromEvent : function(e) {
27407                 var target = e.getTarget();
27408                 while ((target !== null) && (target.parentNode != this.el.dom)) {
27409                 target = target.parentNode;
27410                 }
27411                 if (!target) {
27412                         target = this.el.dom.lastChild || this.el.dom;
27413                 }
27414                 return target;
27415     },
27416
27417 /**
27418  *      Create the drag data which consists of an object which has the property "ddel" as
27419  *      the drag proxy element. 
27420  */
27421     getDragData : function(e) {
27422         var target = this.findItemFromChild(e.getTarget());
27423                 if(target) {
27424                         this.handleSelection(e);
27425                         var selNodes = this.getSelectedNodes();
27426             var dragData = {
27427                 source: this,
27428                 copy: this.copy || (this.allowCopy && e.ctrlKey),
27429                 nodes: selNodes,
27430                 records: []
27431                         };
27432                         var selectedIndices = this.getSelectedIndexes();
27433                         for (var i = 0; i < selectedIndices.length; i++) {
27434                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
27435                         }
27436                         if (selNodes.length == 1) {
27437                                 dragData.ddel = target.cloneNode(true); // the div element
27438                         } else {
27439                                 var div = document.createElement('div'); // create the multi element drag "ghost"
27440                                 div.className = 'multi-proxy';
27441                                 for (var i = 0, len = selNodes.length; i < len; i++) {
27442                                         div.appendChild(selNodes[i].cloneNode(true));
27443                                 }
27444                                 dragData.ddel = div;
27445                         }
27446             //console.log(dragData)
27447             //console.log(dragData.ddel.innerHTML)
27448                         return dragData;
27449                 }
27450         //console.log('nodragData')
27451                 return false;
27452     },
27453     
27454 /**     Specify to which ddGroup items in this DDView may be dragged. */
27455     setDraggable: function(ddGroup) {
27456         if (ddGroup instanceof Array) {
27457                 Roo.each(ddGroup, this.setDraggable, this);
27458                 return;
27459         }
27460         if (this.dragZone) {
27461                 this.dragZone.addToGroup(ddGroup);
27462         } else {
27463                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
27464                                 containerScroll: true,
27465                                 ddGroup: ddGroup 
27466
27467                         });
27468 //                      Draggability implies selection. DragZone's mousedown selects the element.
27469                         if (!this.multiSelect) { this.singleSelect = true; }
27470
27471 //                      Wire the DragZone's handlers up to methods in *this*
27472                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
27473                 }
27474     },
27475
27476 /**     Specify from which ddGroup this DDView accepts drops. */
27477     setDroppable: function(ddGroup) {
27478         if (ddGroup instanceof Array) {
27479                 Roo.each(ddGroup, this.setDroppable, this);
27480                 return;
27481         }
27482         if (this.dropZone) {
27483                 this.dropZone.addToGroup(ddGroup);
27484         } else {
27485                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
27486                                 containerScroll: true,
27487                                 ddGroup: ddGroup
27488                         });
27489
27490 //                      Wire the DropZone's handlers up to methods in *this*
27491                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
27492                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
27493                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
27494                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
27495                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
27496                 }
27497     },
27498
27499 /**     Decide whether to drop above or below a View node. */
27500     getDropPoint : function(e, n, dd){
27501         if (n == this.el.dom) { return "above"; }
27502                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
27503                 var c = t + (b - t) / 2;
27504                 var y = Roo.lib.Event.getPageY(e);
27505                 if(y <= c) {
27506                         return "above";
27507                 }else{
27508                         return "below";
27509                 }
27510     },
27511
27512     onNodeEnter : function(n, dd, e, data){
27513                 return false;
27514     },
27515     
27516     onNodeOver : function(n, dd, e, data){
27517                 var pt = this.getDropPoint(e, n, dd);
27518                 // set the insert point style on the target node
27519                 var dragElClass = this.dropNotAllowed;
27520                 if (pt) {
27521                         var targetElClass;
27522                         if (pt == "above"){
27523                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
27524                                 targetElClass = "x-view-drag-insert-above";
27525                         } else {
27526                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
27527                                 targetElClass = "x-view-drag-insert-below";
27528                         }
27529                         if (this.lastInsertClass != targetElClass){
27530                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
27531                                 this.lastInsertClass = targetElClass;
27532                         }
27533                 }
27534                 return dragElClass;
27535         },
27536
27537     onNodeOut : function(n, dd, e, data){
27538                 this.removeDropIndicators(n);
27539     },
27540
27541     onNodeDrop : function(n, dd, e, data){
27542         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
27543                 return false;
27544         }
27545         var pt = this.getDropPoint(e, n, dd);
27546                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
27547                 if (pt == "below") { insertAt++; }
27548                 for (var i = 0; i < data.records.length; i++) {
27549                         var r = data.records[i];
27550                         var dup = this.store.getById(r.id);
27551                         if (dup && (dd != this.dragZone)) {
27552                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
27553                         } else {
27554                                 if (data.copy) {
27555                                         this.store.insert(insertAt++, r.copy());
27556                                 } else {
27557                                         data.source.isDirtyFlag = true;
27558                                         r.store.remove(r);
27559                                         this.store.insert(insertAt++, r);
27560                                 }
27561                                 this.isDirtyFlag = true;
27562                         }
27563                 }
27564                 this.dragZone.cachedTarget = null;
27565                 return true;
27566     },
27567
27568     removeDropIndicators : function(n){
27569                 if(n){
27570                         Roo.fly(n).removeClass([
27571                                 "x-view-drag-insert-above",
27572                                 "x-view-drag-insert-below"]);
27573                         this.lastInsertClass = "_noclass";
27574                 }
27575     },
27576
27577 /**
27578  *      Utility method. Add a delete option to the DDView's context menu.
27579  *      @param {String} imageUrl The URL of the "delete" icon image.
27580  */
27581         setDeletable: function(imageUrl) {
27582                 if (!this.singleSelect && !this.multiSelect) {
27583                         this.singleSelect = true;
27584                 }
27585                 var c = this.getContextMenu();
27586                 this.contextMenu.on("itemclick", function(item) {
27587                         switch (item.id) {
27588                                 case "delete":
27589                                         this.remove(this.getSelectedIndexes());
27590                                         break;
27591                         }
27592                 }, this);
27593                 this.contextMenu.add({
27594                         icon: imageUrl,
27595                         id: "delete",
27596                         text: 'Delete'
27597                 });
27598         },
27599         
27600 /**     Return the context menu for this DDView. */
27601         getContextMenu: function() {
27602                 if (!this.contextMenu) {
27603 //                      Create the View's context menu
27604                         this.contextMenu = new Roo.menu.Menu({
27605                                 id: this.id + "-contextmenu"
27606                         });
27607                         this.el.on("contextmenu", this.showContextMenu, this);
27608                 }
27609                 return this.contextMenu;
27610         },
27611         
27612         disableContextMenu: function() {
27613                 if (this.contextMenu) {
27614                         this.el.un("contextmenu", this.showContextMenu, this);
27615                 }
27616         },
27617
27618         showContextMenu: function(e, item) {
27619         item = this.findItemFromChild(e.getTarget());
27620                 if (item) {
27621                         e.stopEvent();
27622                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
27623                         this.contextMenu.showAt(e.getXY());
27624             }
27625     },
27626
27627 /**
27628  *      Remove {@link Roo.data.Record}s at the specified indices.
27629  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
27630  */
27631     remove: function(selectedIndices) {
27632                 selectedIndices = [].concat(selectedIndices);
27633                 for (var i = 0; i < selectedIndices.length; i++) {
27634                         var rec = this.store.getAt(selectedIndices[i]);
27635                         this.store.remove(rec);
27636                 }
27637     },
27638
27639 /**
27640  *      Double click fires the event, but also, if this is draggable, and there is only one other
27641  *      related DropZone, it transfers the selected node.
27642  */
27643     onDblClick : function(e){
27644         var item = this.findItemFromChild(e.getTarget());
27645         if(item){
27646             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
27647                 return false;
27648             }
27649             if (this.dragGroup) {
27650                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
27651                     while (targets.indexOf(this.dropZone) > -1) {
27652                             targets.remove(this.dropZone);
27653                                 }
27654                     if (targets.length == 1) {
27655                                         this.dragZone.cachedTarget = null;
27656                         var el = Roo.get(targets[0].getEl());
27657                         var box = el.getBox(true);
27658                         targets[0].onNodeDrop(el.dom, {
27659                                 target: el.dom,
27660                                 xy: [box.x, box.y + box.height - 1]
27661                         }, null, this.getDragData(e));
27662                     }
27663                 }
27664         }
27665     },
27666     
27667     handleSelection: function(e) {
27668                 this.dragZone.cachedTarget = null;
27669         var item = this.findItemFromChild(e.getTarget());
27670         if (!item) {
27671                 this.clearSelections(true);
27672                 return;
27673         }
27674                 if (item && (this.multiSelect || this.singleSelect)){
27675                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
27676                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
27677                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
27678                                 this.unselect(item);
27679                         } else {
27680                                 this.select(item, this.multiSelect && e.ctrlKey);
27681                                 this.lastSelection = item;
27682                         }
27683                 }
27684     },
27685
27686     onItemClick : function(item, index, e){
27687                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
27688                         return false;
27689                 }
27690                 return true;
27691     },
27692
27693     unselect : function(nodeInfo, suppressEvent){
27694                 var node = this.getNode(nodeInfo);
27695                 if(node && this.isSelected(node)){
27696                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27697                                 Roo.fly(node).removeClass(this.selectedClass);
27698                                 this.selections.remove(node);
27699                                 if(!suppressEvent){
27700                                         this.fireEvent("selectionchange", this, this.selections);
27701                                 }
27702                         }
27703                 }
27704     }
27705 });
27706 /*
27707  * Based on:
27708  * Ext JS Library 1.1.1
27709  * Copyright(c) 2006-2007, Ext JS, LLC.
27710  *
27711  * Originally Released Under LGPL - original licence link has changed is not relivant.
27712  *
27713  * Fork - LGPL
27714  * <script type="text/javascript">
27715  */
27716  
27717 /**
27718  * @class Roo.LayoutManager
27719  * @extends Roo.util.Observable
27720  * Base class for layout managers.
27721  */
27722 Roo.LayoutManager = function(container, config){
27723     Roo.LayoutManager.superclass.constructor.call(this);
27724     this.el = Roo.get(container);
27725     // ie scrollbar fix
27726     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
27727         document.body.scroll = "no";
27728     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
27729         this.el.position('relative');
27730     }
27731     this.id = this.el.id;
27732     this.el.addClass("x-layout-container");
27733     /** false to disable window resize monitoring @type Boolean */
27734     this.monitorWindowResize = true;
27735     this.regions = {};
27736     this.addEvents({
27737         /**
27738          * @event layout
27739          * Fires when a layout is performed. 
27740          * @param {Roo.LayoutManager} this
27741          */
27742         "layout" : true,
27743         /**
27744          * @event regionresized
27745          * Fires when the user resizes a region. 
27746          * @param {Roo.LayoutRegion} region The resized region
27747          * @param {Number} newSize The new size (width for east/west, height for north/south)
27748          */
27749         "regionresized" : true,
27750         /**
27751          * @event regioncollapsed
27752          * Fires when a region is collapsed. 
27753          * @param {Roo.LayoutRegion} region The collapsed region
27754          */
27755         "regioncollapsed" : true,
27756         /**
27757          * @event regionexpanded
27758          * Fires when a region is expanded.  
27759          * @param {Roo.LayoutRegion} region The expanded region
27760          */
27761         "regionexpanded" : true
27762     });
27763     this.updating = false;
27764     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
27765 };
27766
27767 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
27768     /**
27769      * Returns true if this layout is currently being updated
27770      * @return {Boolean}
27771      */
27772     isUpdating : function(){
27773         return this.updating; 
27774     },
27775     
27776     /**
27777      * Suspend the LayoutManager from doing auto-layouts while
27778      * making multiple add or remove calls
27779      */
27780     beginUpdate : function(){
27781         this.updating = true;    
27782     },
27783     
27784     /**
27785      * Restore auto-layouts and optionally disable the manager from performing a layout
27786      * @param {Boolean} noLayout true to disable a layout update 
27787      */
27788     endUpdate : function(noLayout){
27789         this.updating = false;
27790         if(!noLayout){
27791             this.layout();
27792         }    
27793     },
27794     
27795     layout: function(){
27796         
27797     },
27798     
27799     onRegionResized : function(region, newSize){
27800         this.fireEvent("regionresized", region, newSize);
27801         this.layout();
27802     },
27803     
27804     onRegionCollapsed : function(region){
27805         this.fireEvent("regioncollapsed", region);
27806     },
27807     
27808     onRegionExpanded : function(region){
27809         this.fireEvent("regionexpanded", region);
27810     },
27811         
27812     /**
27813      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
27814      * performs box-model adjustments.
27815      * @return {Object} The size as an object {width: (the width), height: (the height)}
27816      */
27817     getViewSize : function(){
27818         var size;
27819         if(this.el.dom != document.body){
27820             size = this.el.getSize();
27821         }else{
27822             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
27823         }
27824         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
27825         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27826         return size;
27827     },
27828     
27829     /**
27830      * Returns the Element this layout is bound to.
27831      * @return {Roo.Element}
27832      */
27833     getEl : function(){
27834         return this.el;
27835     },
27836     
27837     /**
27838      * Returns the specified region.
27839      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
27840      * @return {Roo.LayoutRegion}
27841      */
27842     getRegion : function(target){
27843         return this.regions[target.toLowerCase()];
27844     },
27845     
27846     onWindowResize : function(){
27847         if(this.monitorWindowResize){
27848             this.layout();
27849         }
27850     }
27851 });/*
27852  * Based on:
27853  * Ext JS Library 1.1.1
27854  * Copyright(c) 2006-2007, Ext JS, LLC.
27855  *
27856  * Originally Released Under LGPL - original licence link has changed is not relivant.
27857  *
27858  * Fork - LGPL
27859  * <script type="text/javascript">
27860  */
27861 /**
27862  * @class Roo.BorderLayout
27863  * @extends Roo.LayoutManager
27864  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
27865  * please see: <br><br>
27866  * <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>
27867  * <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>
27868  * Example:
27869  <pre><code>
27870  var layout = new Roo.BorderLayout(document.body, {
27871     north: {
27872         initialSize: 25,
27873         titlebar: false
27874     },
27875     west: {
27876         split:true,
27877         initialSize: 200,
27878         minSize: 175,
27879         maxSize: 400,
27880         titlebar: true,
27881         collapsible: true
27882     },
27883     east: {
27884         split:true,
27885         initialSize: 202,
27886         minSize: 175,
27887         maxSize: 400,
27888         titlebar: true,
27889         collapsible: true
27890     },
27891     south: {
27892         split:true,
27893         initialSize: 100,
27894         minSize: 100,
27895         maxSize: 200,
27896         titlebar: true,
27897         collapsible: true
27898     },
27899     center: {
27900         titlebar: true,
27901         autoScroll:true,
27902         resizeTabs: true,
27903         minTabWidth: 50,
27904         preferredTabWidth: 150
27905     }
27906 });
27907
27908 // shorthand
27909 var CP = Roo.ContentPanel;
27910
27911 layout.beginUpdate();
27912 layout.add("north", new CP("north", "North"));
27913 layout.add("south", new CP("south", {title: "South", closable: true}));
27914 layout.add("west", new CP("west", {title: "West"}));
27915 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
27916 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
27917 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
27918 layout.getRegion("center").showPanel("center1");
27919 layout.endUpdate();
27920 </code></pre>
27921
27922 <b>The container the layout is rendered into can be either the body element or any other element.
27923 If it is not the body element, the container needs to either be an absolute positioned element,
27924 or you will need to add "position:relative" to the css of the container.  You will also need to specify
27925 the container size if it is not the body element.</b>
27926
27927 * @constructor
27928 * Create a new BorderLayout
27929 * @param {String/HTMLElement/Element} container The container this layout is bound to
27930 * @param {Object} config Configuration options
27931  */
27932 Roo.BorderLayout = function(container, config){
27933     config = config || {};
27934     Roo.BorderLayout.superclass.constructor.call(this, container, config);
27935     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
27936     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
27937         var target = this.factory.validRegions[i];
27938         if(config[target]){
27939             this.addRegion(target, config[target]);
27940         }
27941     }
27942 };
27943
27944 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
27945     /**
27946      * Creates and adds a new region if it doesn't already exist.
27947      * @param {String} target The target region key (north, south, east, west or center).
27948      * @param {Object} config The regions config object
27949      * @return {BorderLayoutRegion} The new region
27950      */
27951     addRegion : function(target, config){
27952         if(!this.regions[target]){
27953             var r = this.factory.create(target, this, config);
27954             this.bindRegion(target, r);
27955         }
27956         return this.regions[target];
27957     },
27958
27959     // private (kinda)
27960     bindRegion : function(name, r){
27961         this.regions[name] = r;
27962         r.on("visibilitychange", this.layout, this);
27963         r.on("paneladded", this.layout, this);
27964         r.on("panelremoved", this.layout, this);
27965         r.on("invalidated", this.layout, this);
27966         r.on("resized", this.onRegionResized, this);
27967         r.on("collapsed", this.onRegionCollapsed, this);
27968         r.on("expanded", this.onRegionExpanded, this);
27969     },
27970
27971     /**
27972      * Performs a layout update.
27973      */
27974     layout : function(){
27975         if(this.updating) return;
27976         var size = this.getViewSize();
27977         var w = size.width;
27978         var h = size.height;
27979         var centerW = w;
27980         var centerH = h;
27981         var centerY = 0;
27982         var centerX = 0;
27983         //var x = 0, y = 0;
27984
27985         var rs = this.regions;
27986         var north = rs["north"];
27987         var south = rs["south"]; 
27988         var west = rs["west"];
27989         var east = rs["east"];
27990         var center = rs["center"];
27991         //if(this.hideOnLayout){ // not supported anymore
27992             //c.el.setStyle("display", "none");
27993         //}
27994         if(north && north.isVisible()){
27995             var b = north.getBox();
27996             var m = north.getMargins();
27997             b.width = w - (m.left+m.right);
27998             b.x = m.left;
27999             b.y = m.top;
28000             centerY = b.height + b.y + m.bottom;
28001             centerH -= centerY;
28002             north.updateBox(this.safeBox(b));
28003         }
28004         if(south && south.isVisible()){
28005             var b = south.getBox();
28006             var m = south.getMargins();
28007             b.width = w - (m.left+m.right);
28008             b.x = m.left;
28009             var totalHeight = (b.height + m.top + m.bottom);
28010             b.y = h - totalHeight + m.top;
28011             centerH -= totalHeight;
28012             south.updateBox(this.safeBox(b));
28013         }
28014         if(west && west.isVisible()){
28015             var b = west.getBox();
28016             var m = west.getMargins();
28017             b.height = centerH - (m.top+m.bottom);
28018             b.x = m.left;
28019             b.y = centerY + m.top;
28020             var totalWidth = (b.width + m.left + m.right);
28021             centerX += totalWidth;
28022             centerW -= totalWidth;
28023             west.updateBox(this.safeBox(b));
28024         }
28025         if(east && east.isVisible()){
28026             var b = east.getBox();
28027             var m = east.getMargins();
28028             b.height = centerH - (m.top+m.bottom);
28029             var totalWidth = (b.width + m.left + m.right);
28030             b.x = w - totalWidth + m.left;
28031             b.y = centerY + m.top;
28032             centerW -= totalWidth;
28033             east.updateBox(this.safeBox(b));
28034         }
28035         if(center){
28036             var m = center.getMargins();
28037             var centerBox = {
28038                 x: centerX + m.left,
28039                 y: centerY + m.top,
28040                 width: centerW - (m.left+m.right),
28041                 height: centerH - (m.top+m.bottom)
28042             };
28043             //if(this.hideOnLayout){
28044                 //center.el.setStyle("display", "block");
28045             //}
28046             center.updateBox(this.safeBox(centerBox));
28047         }
28048         this.el.repaint();
28049         this.fireEvent("layout", this);
28050     },
28051
28052     // private
28053     safeBox : function(box){
28054         box.width = Math.max(0, box.width);
28055         box.height = Math.max(0, box.height);
28056         return box;
28057     },
28058
28059     /**
28060      * Adds a ContentPanel (or subclass) to this layout.
28061      * @param {String} target The target region key (north, south, east, west or center).
28062      * @param {Roo.ContentPanel} panel The panel to add
28063      * @return {Roo.ContentPanel} The added panel
28064      */
28065     add : function(target, panel){
28066          
28067         target = target.toLowerCase();
28068         return this.regions[target].add(panel);
28069     },
28070
28071     /**
28072      * Remove a ContentPanel (or subclass) to this layout.
28073      * @param {String} target The target region key (north, south, east, west or center).
28074      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28075      * @return {Roo.ContentPanel} The removed panel
28076      */
28077     remove : function(target, panel){
28078         target = target.toLowerCase();
28079         return this.regions[target].remove(panel);
28080     },
28081
28082     /**
28083      * Searches all regions for a panel with the specified id
28084      * @param {String} panelId
28085      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28086      */
28087     findPanel : function(panelId){
28088         var rs = this.regions;
28089         for(var target in rs){
28090             if(typeof rs[target] != "function"){
28091                 var p = rs[target].getPanel(panelId);
28092                 if(p){
28093                     return p;
28094                 }
28095             }
28096         }
28097         return null;
28098     },
28099
28100     /**
28101      * Searches all regions for a panel with the specified id and activates (shows) it.
28102      * @param {String/ContentPanel} panelId The panels id or the panel itself
28103      * @return {Roo.ContentPanel} The shown panel or null
28104      */
28105     showPanel : function(panelId) {
28106       var rs = this.regions;
28107       for(var target in rs){
28108          var r = rs[target];
28109          if(typeof r != "function"){
28110             if(r.hasPanel(panelId)){
28111                return r.showPanel(panelId);
28112             }
28113          }
28114       }
28115       return null;
28116    },
28117
28118    /**
28119      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28120      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28121      */
28122     restoreState : function(provider){
28123         if(!provider){
28124             provider = Roo.state.Manager;
28125         }
28126         var sm = new Roo.LayoutStateManager();
28127         sm.init(this, provider);
28128     },
28129
28130     /**
28131      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28132      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28133      * a valid ContentPanel config object.  Example:
28134      * <pre><code>
28135 // Create the main layout
28136 var layout = new Roo.BorderLayout('main-ct', {
28137     west: {
28138         split:true,
28139         minSize: 175,
28140         titlebar: true
28141     },
28142     center: {
28143         title:'Components'
28144     }
28145 }, 'main-ct');
28146
28147 // Create and add multiple ContentPanels at once via configs
28148 layout.batchAdd({
28149    west: {
28150        id: 'source-files',
28151        autoCreate:true,
28152        title:'Ext Source Files',
28153        autoScroll:true,
28154        fitToFrame:true
28155    },
28156    center : {
28157        el: cview,
28158        autoScroll:true,
28159        fitToFrame:true,
28160        toolbar: tb,
28161        resizeEl:'cbody'
28162    }
28163 });
28164 </code></pre>
28165      * @param {Object} regions An object containing ContentPanel configs by region name
28166      */
28167     batchAdd : function(regions){
28168         this.beginUpdate();
28169         for(var rname in regions){
28170             var lr = this.regions[rname];
28171             if(lr){
28172                 this.addTypedPanels(lr, regions[rname]);
28173             }
28174         }
28175         this.endUpdate();
28176     },
28177
28178     // private
28179     addTypedPanels : function(lr, ps){
28180         if(typeof ps == 'string'){
28181             lr.add(new Roo.ContentPanel(ps));
28182         }
28183         else if(ps instanceof Array){
28184             for(var i =0, len = ps.length; i < len; i++){
28185                 this.addTypedPanels(lr, ps[i]);
28186             }
28187         }
28188         else if(!ps.events){ // raw config?
28189             var el = ps.el;
28190             delete ps.el; // prevent conflict
28191             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28192         }
28193         else {  // panel object assumed!
28194             lr.add(ps);
28195         }
28196     },
28197     /**
28198      * Adds a xtype elements to the layout.
28199      * <pre><code>
28200
28201 layout.addxtype({
28202        xtype : 'ContentPanel',
28203        region: 'west',
28204        items: [ .... ]
28205    }
28206 );
28207
28208 layout.addxtype({
28209         xtype : 'NestedLayoutPanel',
28210         region: 'west',
28211         layout: {
28212            center: { },
28213            west: { }   
28214         },
28215         items : [ ... list of content panels or nested layout panels.. ]
28216    }
28217 );
28218 </code></pre>
28219      * @param {Object} cfg Xtype definition of item to add.
28220      */
28221     addxtype : function(cfg)
28222     {
28223         // basically accepts a pannel...
28224         // can accept a layout region..!?!?
28225        // console.log('BorderLayout add ' + cfg.xtype)
28226         
28227         if (!cfg.xtype.match(/Panel$/)) {
28228             return false;
28229         }
28230         var ret = false;
28231         var region = cfg.region;
28232         delete cfg.region;
28233         
28234           
28235         var xitems = [];
28236         if (cfg.items) {
28237             xitems = cfg.items;
28238             delete cfg.items;
28239         }
28240         
28241         
28242         switch(cfg.xtype) 
28243         {
28244             case 'ContentPanel':  // ContentPanel (el, cfg)
28245                 if(cfg.autoCreate) {
28246                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28247                 } else {
28248                     var el = this.el.createChild();
28249                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28250                 }
28251                 
28252                 this.add(region, ret);
28253                 break;
28254             
28255             
28256             case 'TreePanel': // our new panel!
28257                 cfg.el = this.el.createChild();
28258                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28259                 this.add(region, ret);
28260                 break;
28261             
28262             case 'NestedLayoutPanel': 
28263                 // create a new Layout (which is  a Border Layout...
28264                 var el = this.el.createChild();
28265                 var clayout = cfg.layout;
28266                 delete cfg.layout;
28267                 clayout.items   = clayout.items  || [];
28268                 // replace this exitems with the clayout ones..
28269                 xitems = clayout.items;
28270                  
28271                 
28272                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
28273                     cfg.background = false;
28274                 }
28275                 var layout = new Roo.BorderLayout(el, clayout);
28276                 
28277                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
28278                 //console.log('adding nested layout panel '  + cfg.toSource());
28279                 this.add(region, ret);
28280                 
28281                 break;
28282                 
28283             case 'GridPanel': 
28284             
28285                 // needs grid and region
28286                 
28287                 //var el = this.getRegion(region).el.createChild();
28288                 var el = this.el.createChild();
28289                 // create the grid first...
28290                 
28291                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
28292                 delete cfg.grid;
28293                 if (region == 'center' && this.active ) {
28294                     cfg.background = false;
28295                 }
28296                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
28297                 
28298                 this.add(region, ret);
28299                 if (cfg.background) {
28300                     ret.on('activate', function(gp) {
28301                         if (!gp.grid.rendered) {
28302                             gp.grid.render();
28303                         }
28304                     });
28305                 } else {
28306                     grid.render();
28307                 }
28308                 break;
28309            
28310                
28311                 
28312                 
28313             default: 
28314                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
28315                 return;
28316              // GridPanel (grid, cfg)
28317             
28318         }
28319         this.beginUpdate();
28320         // add children..
28321         Roo.each(xitems, function(i)  {
28322             ret.addxtype(i);
28323         });
28324         this.endUpdate();
28325         return ret;
28326         
28327     }
28328 });
28329
28330 /**
28331  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
28332  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
28333  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
28334  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
28335  * <pre><code>
28336 // shorthand
28337 var CP = Roo.ContentPanel;
28338
28339 var layout = Roo.BorderLayout.create({
28340     north: {
28341         initialSize: 25,
28342         titlebar: false,
28343         panels: [new CP("north", "North")]
28344     },
28345     west: {
28346         split:true,
28347         initialSize: 200,
28348         minSize: 175,
28349         maxSize: 400,
28350         titlebar: true,
28351         collapsible: true,
28352         panels: [new CP("west", {title: "West"})]
28353     },
28354     east: {
28355         split:true,
28356         initialSize: 202,
28357         minSize: 175,
28358         maxSize: 400,
28359         titlebar: true,
28360         collapsible: true,
28361         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
28362     },
28363     south: {
28364         split:true,
28365         initialSize: 100,
28366         minSize: 100,
28367         maxSize: 200,
28368         titlebar: true,
28369         collapsible: true,
28370         panels: [new CP("south", {title: "South", closable: true})]
28371     },
28372     center: {
28373         titlebar: true,
28374         autoScroll:true,
28375         resizeTabs: true,
28376         minTabWidth: 50,
28377         preferredTabWidth: 150,
28378         panels: [
28379             new CP("center1", {title: "Close Me", closable: true}),
28380             new CP("center2", {title: "Center Panel", closable: false})
28381         ]
28382     }
28383 }, document.body);
28384
28385 layout.getRegion("center").showPanel("center1");
28386 </code></pre>
28387  * @param config
28388  * @param targetEl
28389  */
28390 Roo.BorderLayout.create = function(config, targetEl){
28391     var layout = new Roo.BorderLayout(targetEl || document.body, config);
28392     layout.beginUpdate();
28393     var regions = Roo.BorderLayout.RegionFactory.validRegions;
28394     for(var j = 0, jlen = regions.length; j < jlen; j++){
28395         var lr = regions[j];
28396         if(layout.regions[lr] && config[lr].panels){
28397             var r = layout.regions[lr];
28398             var ps = config[lr].panels;
28399             layout.addTypedPanels(r, ps);
28400         }
28401     }
28402     layout.endUpdate();
28403     return layout;
28404 };
28405
28406 // private
28407 Roo.BorderLayout.RegionFactory = {
28408     // private
28409     validRegions : ["north","south","east","west","center"],
28410
28411     // private
28412     create : function(target, mgr, config){
28413         target = target.toLowerCase();
28414         if(config.lightweight || config.basic){
28415             return new Roo.BasicLayoutRegion(mgr, config, target);
28416         }
28417         switch(target){
28418             case "north":
28419                 return new Roo.NorthLayoutRegion(mgr, config);
28420             case "south":
28421                 return new Roo.SouthLayoutRegion(mgr, config);
28422             case "east":
28423                 return new Roo.EastLayoutRegion(mgr, config);
28424             case "west":
28425                 return new Roo.WestLayoutRegion(mgr, config);
28426             case "center":
28427                 return new Roo.CenterLayoutRegion(mgr, config);
28428         }
28429         throw 'Layout region "'+target+'" not supported.';
28430     }
28431 };/*
28432  * Based on:
28433  * Ext JS Library 1.1.1
28434  * Copyright(c) 2006-2007, Ext JS, LLC.
28435  *
28436  * Originally Released Under LGPL - original licence link has changed is not relivant.
28437  *
28438  * Fork - LGPL
28439  * <script type="text/javascript">
28440  */
28441  
28442 /**
28443  * @class Roo.BasicLayoutRegion
28444  * @extends Roo.util.Observable
28445  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
28446  * and does not have a titlebar, tabs or any other features. All it does is size and position 
28447  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
28448  */
28449 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
28450     this.mgr = mgr;
28451     this.position  = pos;
28452     this.events = {
28453         /**
28454          * @scope Roo.BasicLayoutRegion
28455          */
28456         
28457         /**
28458          * @event beforeremove
28459          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
28460          * @param {Roo.LayoutRegion} this
28461          * @param {Roo.ContentPanel} panel The panel
28462          * @param {Object} e The cancel event object
28463          */
28464         "beforeremove" : true,
28465         /**
28466          * @event invalidated
28467          * Fires when the layout for this region is changed.
28468          * @param {Roo.LayoutRegion} this
28469          */
28470         "invalidated" : true,
28471         /**
28472          * @event visibilitychange
28473          * Fires when this region is shown or hidden 
28474          * @param {Roo.LayoutRegion} this
28475          * @param {Boolean} visibility true or false
28476          */
28477         "visibilitychange" : true,
28478         /**
28479          * @event paneladded
28480          * Fires when a panel is added. 
28481          * @param {Roo.LayoutRegion} this
28482          * @param {Roo.ContentPanel} panel The panel
28483          */
28484         "paneladded" : true,
28485         /**
28486          * @event panelremoved
28487          * Fires when a panel is removed. 
28488          * @param {Roo.LayoutRegion} this
28489          * @param {Roo.ContentPanel} panel The panel
28490          */
28491         "panelremoved" : true,
28492         /**
28493          * @event collapsed
28494          * Fires when this region is collapsed.
28495          * @param {Roo.LayoutRegion} this
28496          */
28497         "collapsed" : true,
28498         /**
28499          * @event expanded
28500          * Fires when this region is expanded.
28501          * @param {Roo.LayoutRegion} this
28502          */
28503         "expanded" : true,
28504         /**
28505          * @event slideshow
28506          * Fires when this region is slid into view.
28507          * @param {Roo.LayoutRegion} this
28508          */
28509         "slideshow" : true,
28510         /**
28511          * @event slidehide
28512          * Fires when this region slides out of view. 
28513          * @param {Roo.LayoutRegion} this
28514          */
28515         "slidehide" : true,
28516         /**
28517          * @event panelactivated
28518          * Fires when a panel is activated. 
28519          * @param {Roo.LayoutRegion} this
28520          * @param {Roo.ContentPanel} panel The activated panel
28521          */
28522         "panelactivated" : true,
28523         /**
28524          * @event resized
28525          * Fires when the user resizes this region. 
28526          * @param {Roo.LayoutRegion} this
28527          * @param {Number} newSize The new size (width for east/west, height for north/south)
28528          */
28529         "resized" : true
28530     };
28531     /** A collection of panels in this region. @type Roo.util.MixedCollection */
28532     this.panels = new Roo.util.MixedCollection();
28533     this.panels.getKey = this.getPanelId.createDelegate(this);
28534     this.box = null;
28535     this.activePanel = null;
28536     // ensure listeners are added...
28537     
28538     if (config.listeners || config.events) {
28539         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
28540             listeners : config.listeners || {},
28541             events : config.events || {}
28542         });
28543     }
28544     
28545     if(skipConfig !== true){
28546         this.applyConfig(config);
28547     }
28548 };
28549
28550 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
28551     getPanelId : function(p){
28552         return p.getId();
28553     },
28554     
28555     applyConfig : function(config){
28556         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
28557         this.config = config;
28558         
28559     },
28560     
28561     /**
28562      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
28563      * the width, for horizontal (north, south) the height.
28564      * @param {Number} newSize The new width or height
28565      */
28566     resizeTo : function(newSize){
28567         var el = this.el ? this.el :
28568                  (this.activePanel ? this.activePanel.getEl() : null);
28569         if(el){
28570             switch(this.position){
28571                 case "east":
28572                 case "west":
28573                     el.setWidth(newSize);
28574                     this.fireEvent("resized", this, newSize);
28575                 break;
28576                 case "north":
28577                 case "south":
28578                     el.setHeight(newSize);
28579                     this.fireEvent("resized", this, newSize);
28580                 break;                
28581             }
28582         }
28583     },
28584     
28585     getBox : function(){
28586         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
28587     },
28588     
28589     getMargins : function(){
28590         return this.margins;
28591     },
28592     
28593     updateBox : function(box){
28594         this.box = box;
28595         var el = this.activePanel.getEl();
28596         el.dom.style.left = box.x + "px";
28597         el.dom.style.top = box.y + "px";
28598         this.activePanel.setSize(box.width, box.height);
28599     },
28600     
28601     /**
28602      * Returns the container element for this region.
28603      * @return {Roo.Element}
28604      */
28605     getEl : function(){
28606         return this.activePanel;
28607     },
28608     
28609     /**
28610      * Returns true if this region is currently visible.
28611      * @return {Boolean}
28612      */
28613     isVisible : function(){
28614         return this.activePanel ? true : false;
28615     },
28616     
28617     setActivePanel : function(panel){
28618         panel = this.getPanel(panel);
28619         if(this.activePanel && this.activePanel != panel){
28620             this.activePanel.setActiveState(false);
28621             this.activePanel.getEl().setLeftTop(-10000,-10000);
28622         }
28623         this.activePanel = panel;
28624         panel.setActiveState(true);
28625         if(this.box){
28626             panel.setSize(this.box.width, this.box.height);
28627         }
28628         this.fireEvent("panelactivated", this, panel);
28629         this.fireEvent("invalidated");
28630     },
28631     
28632     /**
28633      * Show the specified panel.
28634      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
28635      * @return {Roo.ContentPanel} The shown panel or null
28636      */
28637     showPanel : function(panel){
28638         if(panel = this.getPanel(panel)){
28639             this.setActivePanel(panel);
28640         }
28641         return panel;
28642     },
28643     
28644     /**
28645      * Get the active panel for this region.
28646      * @return {Roo.ContentPanel} The active panel or null
28647      */
28648     getActivePanel : function(){
28649         return this.activePanel;
28650     },
28651     
28652     /**
28653      * Add the passed ContentPanel(s)
28654      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
28655      * @return {Roo.ContentPanel} The panel added (if only one was added)
28656      */
28657     add : function(panel){
28658         if(arguments.length > 1){
28659             for(var i = 0, len = arguments.length; i < len; i++) {
28660                 this.add(arguments[i]);
28661             }
28662             return null;
28663         }
28664         if(this.hasPanel(panel)){
28665             this.showPanel(panel);
28666             return panel;
28667         }
28668         var el = panel.getEl();
28669         if(el.dom.parentNode != this.mgr.el.dom){
28670             this.mgr.el.dom.appendChild(el.dom);
28671         }
28672         if(panel.setRegion){
28673             panel.setRegion(this);
28674         }
28675         this.panels.add(panel);
28676         el.setStyle("position", "absolute");
28677         if(!panel.background){
28678             this.setActivePanel(panel);
28679             if(this.config.initialSize && this.panels.getCount()==1){
28680                 this.resizeTo(this.config.initialSize);
28681             }
28682         }
28683         this.fireEvent("paneladded", this, panel);
28684         return panel;
28685     },
28686     
28687     /**
28688      * Returns true if the panel is in this region.
28689      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28690      * @return {Boolean}
28691      */
28692     hasPanel : function(panel){
28693         if(typeof panel == "object"){ // must be panel obj
28694             panel = panel.getId();
28695         }
28696         return this.getPanel(panel) ? true : false;
28697     },
28698     
28699     /**
28700      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
28701      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28702      * @param {Boolean} preservePanel Overrides the config preservePanel option
28703      * @return {Roo.ContentPanel} The panel that was removed
28704      */
28705     remove : function(panel, preservePanel){
28706         panel = this.getPanel(panel);
28707         if(!panel){
28708             return null;
28709         }
28710         var e = {};
28711         this.fireEvent("beforeremove", this, panel, e);
28712         if(e.cancel === true){
28713             return null;
28714         }
28715         var panelId = panel.getId();
28716         this.panels.removeKey(panelId);
28717         return panel;
28718     },
28719     
28720     /**
28721      * Returns the panel specified or null if it's not in this region.
28722      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28723      * @return {Roo.ContentPanel}
28724      */
28725     getPanel : function(id){
28726         if(typeof id == "object"){ // must be panel obj
28727             return id;
28728         }
28729         return this.panels.get(id);
28730     },
28731     
28732     /**
28733      * Returns this regions position (north/south/east/west/center).
28734      * @return {String} 
28735      */
28736     getPosition: function(){
28737         return this.position;    
28738     }
28739 });/*
28740  * Based on:
28741  * Ext JS Library 1.1.1
28742  * Copyright(c) 2006-2007, Ext JS, LLC.
28743  *
28744  * Originally Released Under LGPL - original licence link has changed is not relivant.
28745  *
28746  * Fork - LGPL
28747  * <script type="text/javascript">
28748  */
28749  
28750 /**
28751  * @class Roo.LayoutRegion
28752  * @extends Roo.BasicLayoutRegion
28753  * This class represents a region in a layout manager.
28754  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
28755  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
28756  * @cfg {Boolean} floatable False to disable floating (defaults to true)
28757  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
28758  * @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})
28759  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
28760  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
28761  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
28762  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
28763  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
28764  * @cfg {String} title The title for the region (overrides panel titles)
28765  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
28766  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
28767  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
28768  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
28769  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
28770  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
28771  * the space available, similar to FireFox 1.5 tabs (defaults to false)
28772  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
28773  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
28774  * @cfg {Boolean} showPin True to show a pin button
28775 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
28776 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
28777 * @cfg {Boolean} disableTabTips True to disable tab tooltips
28778 * @cfg {Number} width  For East/West panels
28779 * @cfg {Number} height For North/South panels
28780 * @cfg {Boolean} split To show the splitter
28781  */
28782 Roo.LayoutRegion = function(mgr, config, pos){
28783     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
28784     var dh = Roo.DomHelper;
28785     /** This region's container element 
28786     * @type Roo.Element */
28787     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
28788     /** This region's title element 
28789     * @type Roo.Element */
28790
28791     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
28792         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
28793         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
28794     ]}, true);
28795     this.titleEl.enableDisplayMode();
28796     /** This region's title text element 
28797     * @type HTMLElement */
28798     this.titleTextEl = this.titleEl.dom.firstChild;
28799     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
28800     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
28801     this.closeBtn.enableDisplayMode();
28802     this.closeBtn.on("click", this.closeClicked, this);
28803     this.closeBtn.hide();
28804
28805     this.createBody(config);
28806     this.visible = true;
28807     this.collapsed = false;
28808
28809     if(config.hideWhenEmpty){
28810         this.hide();
28811         this.on("paneladded", this.validateVisibility, this);
28812         this.on("panelremoved", this.validateVisibility, this);
28813     }
28814     this.applyConfig(config);
28815 };
28816
28817 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
28818
28819     createBody : function(){
28820         /** This region's body element 
28821         * @type Roo.Element */
28822         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
28823     },
28824
28825     applyConfig : function(c){
28826         if(c.collapsible && this.position != "center" && !this.collapsedEl){
28827             var dh = Roo.DomHelper;
28828             if(c.titlebar !== false){
28829                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
28830                 this.collapseBtn.on("click", this.collapse, this);
28831                 this.collapseBtn.enableDisplayMode();
28832
28833                 if(c.showPin === true || this.showPin){
28834                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
28835                     this.stickBtn.enableDisplayMode();
28836                     this.stickBtn.on("click", this.expand, this);
28837                     this.stickBtn.hide();
28838                 }
28839             }
28840             /** This region's collapsed element
28841             * @type Roo.Element */
28842             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
28843                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
28844             ]}, true);
28845             if(c.floatable !== false){
28846                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
28847                this.collapsedEl.on("click", this.collapseClick, this);
28848             }
28849
28850             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
28851                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
28852                    id: "message", unselectable: "on", style:{"float":"left"}});
28853                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
28854              }
28855             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
28856             this.expandBtn.on("click", this.expand, this);
28857         }
28858         if(this.collapseBtn){
28859             this.collapseBtn.setVisible(c.collapsible == true);
28860         }
28861         this.cmargins = c.cmargins || this.cmargins ||
28862                          (this.position == "west" || this.position == "east" ?
28863                              {top: 0, left: 2, right:2, bottom: 0} :
28864                              {top: 2, left: 0, right:0, bottom: 2});
28865         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
28866         this.bottomTabs = c.tabPosition != "top";
28867         this.autoScroll = c.autoScroll || false;
28868         if(this.autoScroll){
28869             this.bodyEl.setStyle("overflow", "auto");
28870         }else{
28871             this.bodyEl.setStyle("overflow", "hidden");
28872         }
28873         //if(c.titlebar !== false){
28874             if((!c.titlebar && !c.title) || c.titlebar === false){
28875                 this.titleEl.hide();
28876             }else{
28877                 this.titleEl.show();
28878                 if(c.title){
28879                     this.titleTextEl.innerHTML = c.title;
28880                 }
28881             }
28882         //}
28883         this.duration = c.duration || .30;
28884         this.slideDuration = c.slideDuration || .45;
28885         this.config = c;
28886         if(c.collapsed){
28887             this.collapse(true);
28888         }
28889         if(c.hidden){
28890             this.hide();
28891         }
28892     },
28893     /**
28894      * Returns true if this region is currently visible.
28895      * @return {Boolean}
28896      */
28897     isVisible : function(){
28898         return this.visible;
28899     },
28900
28901     /**
28902      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
28903      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
28904      */
28905     setCollapsedTitle : function(title){
28906         title = title || "&#160;";
28907         if(this.collapsedTitleTextEl){
28908             this.collapsedTitleTextEl.innerHTML = title;
28909         }
28910     },
28911
28912     getBox : function(){
28913         var b;
28914         if(!this.collapsed){
28915             b = this.el.getBox(false, true);
28916         }else{
28917             b = this.collapsedEl.getBox(false, true);
28918         }
28919         return b;
28920     },
28921
28922     getMargins : function(){
28923         return this.collapsed ? this.cmargins : this.margins;
28924     },
28925
28926     highlight : function(){
28927         this.el.addClass("x-layout-panel-dragover");
28928     },
28929
28930     unhighlight : function(){
28931         this.el.removeClass("x-layout-panel-dragover");
28932     },
28933
28934     updateBox : function(box){
28935         this.box = box;
28936         if(!this.collapsed){
28937             this.el.dom.style.left = box.x + "px";
28938             this.el.dom.style.top = box.y + "px";
28939             this.updateBody(box.width, box.height);
28940         }else{
28941             this.collapsedEl.dom.style.left = box.x + "px";
28942             this.collapsedEl.dom.style.top = box.y + "px";
28943             this.collapsedEl.setSize(box.width, box.height);
28944         }
28945         if(this.tabs){
28946             this.tabs.autoSizeTabs();
28947         }
28948     },
28949
28950     updateBody : function(w, h){
28951         if(w !== null){
28952             this.el.setWidth(w);
28953             w -= this.el.getBorderWidth("rl");
28954             if(this.config.adjustments){
28955                 w += this.config.adjustments[0];
28956             }
28957         }
28958         if(h !== null){
28959             this.el.setHeight(h);
28960             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
28961             h -= this.el.getBorderWidth("tb");
28962             if(this.config.adjustments){
28963                 h += this.config.adjustments[1];
28964             }
28965             this.bodyEl.setHeight(h);
28966             if(this.tabs){
28967                 h = this.tabs.syncHeight(h);
28968             }
28969         }
28970         if(this.panelSize){
28971             w = w !== null ? w : this.panelSize.width;
28972             h = h !== null ? h : this.panelSize.height;
28973         }
28974         if(this.activePanel){
28975             var el = this.activePanel.getEl();
28976             w = w !== null ? w : el.getWidth();
28977             h = h !== null ? h : el.getHeight();
28978             this.panelSize = {width: w, height: h};
28979             this.activePanel.setSize(w, h);
28980         }
28981         if(Roo.isIE && this.tabs){
28982             this.tabs.el.repaint();
28983         }
28984     },
28985
28986     /**
28987      * Returns the container element for this region.
28988      * @return {Roo.Element}
28989      */
28990     getEl : function(){
28991         return this.el;
28992     },
28993
28994     /**
28995      * Hides this region.
28996      */
28997     hide : function(){
28998         if(!this.collapsed){
28999             this.el.dom.style.left = "-2000px";
29000             this.el.hide();
29001         }else{
29002             this.collapsedEl.dom.style.left = "-2000px";
29003             this.collapsedEl.hide();
29004         }
29005         this.visible = false;
29006         this.fireEvent("visibilitychange", this, false);
29007     },
29008
29009     /**
29010      * Shows this region if it was previously hidden.
29011      */
29012     show : function(){
29013         if(!this.collapsed){
29014             this.el.show();
29015         }else{
29016             this.collapsedEl.show();
29017         }
29018         this.visible = true;
29019         this.fireEvent("visibilitychange", this, true);
29020     },
29021
29022     closeClicked : function(){
29023         if(this.activePanel){
29024             this.remove(this.activePanel);
29025         }
29026     },
29027
29028     collapseClick : function(e){
29029         if(this.isSlid){
29030            e.stopPropagation();
29031            this.slideIn();
29032         }else{
29033            e.stopPropagation();
29034            this.slideOut();
29035         }
29036     },
29037
29038     /**
29039      * Collapses this region.
29040      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29041      */
29042     collapse : function(skipAnim){
29043         if(this.collapsed) return;
29044         this.collapsed = true;
29045         if(this.split){
29046             this.split.el.hide();
29047         }
29048         if(this.config.animate && skipAnim !== true){
29049             this.fireEvent("invalidated", this);
29050             this.animateCollapse();
29051         }else{
29052             this.el.setLocation(-20000,-20000);
29053             this.el.hide();
29054             this.collapsedEl.show();
29055             this.fireEvent("collapsed", this);
29056             this.fireEvent("invalidated", this);
29057         }
29058     },
29059
29060     animateCollapse : function(){
29061         // overridden
29062     },
29063
29064     /**
29065      * Expands this region if it was previously collapsed.
29066      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29067      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29068      */
29069     expand : function(e, skipAnim){
29070         if(e) e.stopPropagation();
29071         if(!this.collapsed || this.el.hasActiveFx()) return;
29072         if(this.isSlid){
29073             this.afterSlideIn();
29074             skipAnim = true;
29075         }
29076         this.collapsed = false;
29077         if(this.config.animate && skipAnim !== true){
29078             this.animateExpand();
29079         }else{
29080             this.el.show();
29081             if(this.split){
29082                 this.split.el.show();
29083             }
29084             this.collapsedEl.setLocation(-2000,-2000);
29085             this.collapsedEl.hide();
29086             this.fireEvent("invalidated", this);
29087             this.fireEvent("expanded", this);
29088         }
29089     },
29090
29091     animateExpand : function(){
29092         // overridden
29093     },
29094
29095     initTabs : function(){
29096         this.bodyEl.setStyle("overflow", "hidden");
29097         var ts = new Roo.TabPanel(this.bodyEl.dom, {
29098             tabPosition: this.bottomTabs ? 'bottom' : 'top',
29099             disableTooltips: this.config.disableTabTips
29100         });
29101         if(this.config.hideTabs){
29102             ts.stripWrap.setDisplayed(false);
29103         }
29104         this.tabs = ts;
29105         ts.resizeTabs = this.config.resizeTabs === true;
29106         ts.minTabWidth = this.config.minTabWidth || 40;
29107         ts.maxTabWidth = this.config.maxTabWidth || 250;
29108         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29109         ts.monitorResize = false;
29110         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29111         ts.bodyEl.addClass('x-layout-tabs-body');
29112         this.panels.each(this.initPanelAsTab, this);
29113     },
29114
29115     initPanelAsTab : function(panel){
29116         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29117                     this.config.closeOnTab && panel.isClosable());
29118         if(panel.tabTip !== undefined){
29119             ti.setTooltip(panel.tabTip);
29120         }
29121         ti.on("activate", function(){
29122               this.setActivePanel(panel);
29123         }, this);
29124         if(this.config.closeOnTab){
29125             ti.on("beforeclose", function(t, e){
29126                 e.cancel = true;
29127                 this.remove(panel);
29128             }, this);
29129         }
29130         return ti;
29131     },
29132
29133     updatePanelTitle : function(panel, title){
29134         if(this.activePanel == panel){
29135             this.updateTitle(title);
29136         }
29137         if(this.tabs){
29138             var ti = this.tabs.getTab(panel.getEl().id);
29139             ti.setText(title);
29140             if(panel.tabTip !== undefined){
29141                 ti.setTooltip(panel.tabTip);
29142             }
29143         }
29144     },
29145
29146     updateTitle : function(title){
29147         if(this.titleTextEl && !this.config.title){
29148             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29149         }
29150     },
29151
29152     setActivePanel : function(panel){
29153         panel = this.getPanel(panel);
29154         if(this.activePanel && this.activePanel != panel){
29155             this.activePanel.setActiveState(false);
29156         }
29157         this.activePanel = panel;
29158         panel.setActiveState(true);
29159         if(this.panelSize){
29160             panel.setSize(this.panelSize.width, this.panelSize.height);
29161         }
29162         if(this.closeBtn){
29163             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29164         }
29165         this.updateTitle(panel.getTitle());
29166         if(this.tabs){
29167             this.fireEvent("invalidated", this);
29168         }
29169         this.fireEvent("panelactivated", this, panel);
29170     },
29171
29172     /**
29173      * Shows the specified panel.
29174      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29175      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29176      */
29177     showPanel : function(panel){
29178         if(panel = this.getPanel(panel)){
29179             if(this.tabs){
29180                 var tab = this.tabs.getTab(panel.getEl().id);
29181                 if(tab.isHidden()){
29182                     this.tabs.unhideTab(tab.id);
29183                 }
29184                 tab.activate();
29185             }else{
29186                 this.setActivePanel(panel);
29187             }
29188         }
29189         return panel;
29190     },
29191
29192     /**
29193      * Get the active panel for this region.
29194      * @return {Roo.ContentPanel} The active panel or null
29195      */
29196     getActivePanel : function(){
29197         return this.activePanel;
29198     },
29199
29200     validateVisibility : function(){
29201         if(this.panels.getCount() < 1){
29202             this.updateTitle("&#160;");
29203             this.closeBtn.hide();
29204             this.hide();
29205         }else{
29206             if(!this.isVisible()){
29207                 this.show();
29208             }
29209         }
29210     },
29211
29212     /**
29213      * Adds the passed ContentPanel(s) to this region.
29214      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29215      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
29216      */
29217     add : function(panel){
29218         if(arguments.length > 1){
29219             for(var i = 0, len = arguments.length; i < len; i++) {
29220                 this.add(arguments[i]);
29221             }
29222             return null;
29223         }
29224         if(this.hasPanel(panel)){
29225             this.showPanel(panel);
29226             return panel;
29227         }
29228         panel.setRegion(this);
29229         this.panels.add(panel);
29230         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
29231             this.bodyEl.dom.appendChild(panel.getEl().dom);
29232             if(panel.background !== true){
29233                 this.setActivePanel(panel);
29234             }
29235             this.fireEvent("paneladded", this, panel);
29236             return panel;
29237         }
29238         if(!this.tabs){
29239             this.initTabs();
29240         }else{
29241             this.initPanelAsTab(panel);
29242         }
29243         if(panel.background !== true){
29244             this.tabs.activate(panel.getEl().id);
29245         }
29246         this.fireEvent("paneladded", this, panel);
29247         return panel;
29248     },
29249
29250     /**
29251      * Hides the tab for the specified panel.
29252      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29253      */
29254     hidePanel : function(panel){
29255         if(this.tabs && (panel = this.getPanel(panel))){
29256             this.tabs.hideTab(panel.getEl().id);
29257         }
29258     },
29259
29260     /**
29261      * Unhides the tab for a previously hidden panel.
29262      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29263      */
29264     unhidePanel : function(panel){
29265         if(this.tabs && (panel = this.getPanel(panel))){
29266             this.tabs.unhideTab(panel.getEl().id);
29267         }
29268     },
29269
29270     clearPanels : function(){
29271         while(this.panels.getCount() > 0){
29272              this.remove(this.panels.first());
29273         }
29274     },
29275
29276     /**
29277      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29278      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29279      * @param {Boolean} preservePanel Overrides the config preservePanel option
29280      * @return {Roo.ContentPanel} The panel that was removed
29281      */
29282     remove : function(panel, preservePanel){
29283         panel = this.getPanel(panel);
29284         if(!panel){
29285             return null;
29286         }
29287         var e = {};
29288         this.fireEvent("beforeremove", this, panel, e);
29289         if(e.cancel === true){
29290             return null;
29291         }
29292         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
29293         var panelId = panel.getId();
29294         this.panels.removeKey(panelId);
29295         if(preservePanel){
29296             document.body.appendChild(panel.getEl().dom);
29297         }
29298         if(this.tabs){
29299             this.tabs.removeTab(panel.getEl().id);
29300         }else if (!preservePanel){
29301             this.bodyEl.dom.removeChild(panel.getEl().dom);
29302         }
29303         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
29304             var p = this.panels.first();
29305             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
29306             tempEl.appendChild(p.getEl().dom);
29307             this.bodyEl.update("");
29308             this.bodyEl.dom.appendChild(p.getEl().dom);
29309             tempEl = null;
29310             this.updateTitle(p.getTitle());
29311             this.tabs = null;
29312             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29313             this.setActivePanel(p);
29314         }
29315         panel.setRegion(null);
29316         if(this.activePanel == panel){
29317             this.activePanel = null;
29318         }
29319         if(this.config.autoDestroy !== false && preservePanel !== true){
29320             try{panel.destroy();}catch(e){}
29321         }
29322         this.fireEvent("panelremoved", this, panel);
29323         return panel;
29324     },
29325
29326     /**
29327      * Returns the TabPanel component used by this region
29328      * @return {Roo.TabPanel}
29329      */
29330     getTabs : function(){
29331         return this.tabs;
29332     },
29333
29334     createTool : function(parentEl, className){
29335         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
29336             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
29337         btn.addClassOnOver("x-layout-tools-button-over");
29338         return btn;
29339     }
29340 });/*
29341  * Based on:
29342  * Ext JS Library 1.1.1
29343  * Copyright(c) 2006-2007, Ext JS, LLC.
29344  *
29345  * Originally Released Under LGPL - original licence link has changed is not relivant.
29346  *
29347  * Fork - LGPL
29348  * <script type="text/javascript">
29349  */
29350  
29351
29352
29353 /**
29354  * @class Roo.SplitLayoutRegion
29355  * @extends Roo.LayoutRegion
29356  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
29357  */
29358 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
29359     this.cursor = cursor;
29360     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
29361 };
29362
29363 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
29364     splitTip : "Drag to resize.",
29365     collapsibleSplitTip : "Drag to resize. Double click to hide.",
29366     useSplitTips : false,
29367
29368     applyConfig : function(config){
29369         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
29370         if(config.split){
29371             if(!this.split){
29372                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
29373                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
29374                 /** The SplitBar for this region 
29375                 * @type Roo.SplitBar */
29376                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
29377                 this.split.on("moved", this.onSplitMove, this);
29378                 this.split.useShim = config.useShim === true;
29379                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
29380                 if(this.useSplitTips){
29381                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
29382                 }
29383                 if(config.collapsible){
29384                     this.split.el.on("dblclick", this.collapse,  this);
29385                 }
29386             }
29387             if(typeof config.minSize != "undefined"){
29388                 this.split.minSize = config.minSize;
29389             }
29390             if(typeof config.maxSize != "undefined"){
29391                 this.split.maxSize = config.maxSize;
29392             }
29393             if(config.hideWhenEmpty || config.hidden || config.collapsed){
29394                 this.hideSplitter();
29395             }
29396         }
29397     },
29398
29399     getHMaxSize : function(){
29400          var cmax = this.config.maxSize || 10000;
29401          var center = this.mgr.getRegion("center");
29402          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
29403     },
29404
29405     getVMaxSize : function(){
29406          var cmax = this.config.maxSize || 10000;
29407          var center = this.mgr.getRegion("center");
29408          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
29409     },
29410
29411     onSplitMove : function(split, newSize){
29412         this.fireEvent("resized", this, newSize);
29413     },
29414     
29415     /** 
29416      * Returns the {@link Roo.SplitBar} for this region.
29417      * @return {Roo.SplitBar}
29418      */
29419     getSplitBar : function(){
29420         return this.split;
29421     },
29422     
29423     hide : function(){
29424         this.hideSplitter();
29425         Roo.SplitLayoutRegion.superclass.hide.call(this);
29426     },
29427
29428     hideSplitter : function(){
29429         if(this.split){
29430             this.split.el.setLocation(-2000,-2000);
29431             this.split.el.hide();
29432         }
29433     },
29434
29435     show : function(){
29436         if(this.split){
29437             this.split.el.show();
29438         }
29439         Roo.SplitLayoutRegion.superclass.show.call(this);
29440     },
29441     
29442     beforeSlide: function(){
29443         if(Roo.isGecko){// firefox overflow auto bug workaround
29444             this.bodyEl.clip();
29445             if(this.tabs) this.tabs.bodyEl.clip();
29446             if(this.activePanel){
29447                 this.activePanel.getEl().clip();
29448                 
29449                 if(this.activePanel.beforeSlide){
29450                     this.activePanel.beforeSlide();
29451                 }
29452             }
29453         }
29454     },
29455     
29456     afterSlide : function(){
29457         if(Roo.isGecko){// firefox overflow auto bug workaround
29458             this.bodyEl.unclip();
29459             if(this.tabs) this.tabs.bodyEl.unclip();
29460             if(this.activePanel){
29461                 this.activePanel.getEl().unclip();
29462                 if(this.activePanel.afterSlide){
29463                     this.activePanel.afterSlide();
29464                 }
29465             }
29466         }
29467     },
29468
29469     initAutoHide : function(){
29470         if(this.autoHide !== false){
29471             if(!this.autoHideHd){
29472                 var st = new Roo.util.DelayedTask(this.slideIn, this);
29473                 this.autoHideHd = {
29474                     "mouseout": function(e){
29475                         if(!e.within(this.el, true)){
29476                             st.delay(500);
29477                         }
29478                     },
29479                     "mouseover" : function(e){
29480                         st.cancel();
29481                     },
29482                     scope : this
29483                 };
29484             }
29485             this.el.on(this.autoHideHd);
29486         }
29487     },
29488
29489     clearAutoHide : function(){
29490         if(this.autoHide !== false){
29491             this.el.un("mouseout", this.autoHideHd.mouseout);
29492             this.el.un("mouseover", this.autoHideHd.mouseover);
29493         }
29494     },
29495
29496     clearMonitor : function(){
29497         Roo.get(document).un("click", this.slideInIf, this);
29498     },
29499
29500     // these names are backwards but not changed for compat
29501     slideOut : function(){
29502         if(this.isSlid || this.el.hasActiveFx()){
29503             return;
29504         }
29505         this.isSlid = true;
29506         if(this.collapseBtn){
29507             this.collapseBtn.hide();
29508         }
29509         this.closeBtnState = this.closeBtn.getStyle('display');
29510         this.closeBtn.hide();
29511         if(this.stickBtn){
29512             this.stickBtn.show();
29513         }
29514         this.el.show();
29515         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
29516         this.beforeSlide();
29517         this.el.setStyle("z-index", 10001);
29518         this.el.slideIn(this.getSlideAnchor(), {
29519             callback: function(){
29520                 this.afterSlide();
29521                 this.initAutoHide();
29522                 Roo.get(document).on("click", this.slideInIf, this);
29523                 this.fireEvent("slideshow", this);
29524             },
29525             scope: this,
29526             block: true
29527         });
29528     },
29529
29530     afterSlideIn : function(){
29531         this.clearAutoHide();
29532         this.isSlid = false;
29533         this.clearMonitor();
29534         this.el.setStyle("z-index", "");
29535         if(this.collapseBtn){
29536             this.collapseBtn.show();
29537         }
29538         this.closeBtn.setStyle('display', this.closeBtnState);
29539         if(this.stickBtn){
29540             this.stickBtn.hide();
29541         }
29542         this.fireEvent("slidehide", this);
29543     },
29544
29545     slideIn : function(cb){
29546         if(!this.isSlid || this.el.hasActiveFx()){
29547             Roo.callback(cb);
29548             return;
29549         }
29550         this.isSlid = false;
29551         this.beforeSlide();
29552         this.el.slideOut(this.getSlideAnchor(), {
29553             callback: function(){
29554                 this.el.setLeftTop(-10000, -10000);
29555                 this.afterSlide();
29556                 this.afterSlideIn();
29557                 Roo.callback(cb);
29558             },
29559             scope: this,
29560             block: true
29561         });
29562     },
29563     
29564     slideInIf : function(e){
29565         if(!e.within(this.el)){
29566             this.slideIn();
29567         }
29568     },
29569
29570     animateCollapse : function(){
29571         this.beforeSlide();
29572         this.el.setStyle("z-index", 20000);
29573         var anchor = this.getSlideAnchor();
29574         this.el.slideOut(anchor, {
29575             callback : function(){
29576                 this.el.setStyle("z-index", "");
29577                 this.collapsedEl.slideIn(anchor, {duration:.3});
29578                 this.afterSlide();
29579                 this.el.setLocation(-10000,-10000);
29580                 this.el.hide();
29581                 this.fireEvent("collapsed", this);
29582             },
29583             scope: this,
29584             block: true
29585         });
29586     },
29587
29588     animateExpand : function(){
29589         this.beforeSlide();
29590         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
29591         this.el.setStyle("z-index", 20000);
29592         this.collapsedEl.hide({
29593             duration:.1
29594         });
29595         this.el.slideIn(this.getSlideAnchor(), {
29596             callback : function(){
29597                 this.el.setStyle("z-index", "");
29598                 this.afterSlide();
29599                 if(this.split){
29600                     this.split.el.show();
29601                 }
29602                 this.fireEvent("invalidated", this);
29603                 this.fireEvent("expanded", this);
29604             },
29605             scope: this,
29606             block: true
29607         });
29608     },
29609
29610     anchors : {
29611         "west" : "left",
29612         "east" : "right",
29613         "north" : "top",
29614         "south" : "bottom"
29615     },
29616
29617     sanchors : {
29618         "west" : "l",
29619         "east" : "r",
29620         "north" : "t",
29621         "south" : "b"
29622     },
29623
29624     canchors : {
29625         "west" : "tl-tr",
29626         "east" : "tr-tl",
29627         "north" : "tl-bl",
29628         "south" : "bl-tl"
29629     },
29630
29631     getAnchor : function(){
29632         return this.anchors[this.position];
29633     },
29634
29635     getCollapseAnchor : function(){
29636         return this.canchors[this.position];
29637     },
29638
29639     getSlideAnchor : function(){
29640         return this.sanchors[this.position];
29641     },
29642
29643     getAlignAdj : function(){
29644         var cm = this.cmargins;
29645         switch(this.position){
29646             case "west":
29647                 return [0, 0];
29648             break;
29649             case "east":
29650                 return [0, 0];
29651             break;
29652             case "north":
29653                 return [0, 0];
29654             break;
29655             case "south":
29656                 return [0, 0];
29657             break;
29658         }
29659     },
29660
29661     getExpandAdj : function(){
29662         var c = this.collapsedEl, cm = this.cmargins;
29663         switch(this.position){
29664             case "west":
29665                 return [-(cm.right+c.getWidth()+cm.left), 0];
29666             break;
29667             case "east":
29668                 return [cm.right+c.getWidth()+cm.left, 0];
29669             break;
29670             case "north":
29671                 return [0, -(cm.top+cm.bottom+c.getHeight())];
29672             break;
29673             case "south":
29674                 return [0, cm.top+cm.bottom+c.getHeight()];
29675             break;
29676         }
29677     }
29678 });/*
29679  * Based on:
29680  * Ext JS Library 1.1.1
29681  * Copyright(c) 2006-2007, Ext JS, LLC.
29682  *
29683  * Originally Released Under LGPL - original licence link has changed is not relivant.
29684  *
29685  * Fork - LGPL
29686  * <script type="text/javascript">
29687  */
29688 /*
29689  * These classes are private internal classes
29690  */
29691 Roo.CenterLayoutRegion = function(mgr, config){
29692     Roo.LayoutRegion.call(this, mgr, config, "center");
29693     this.visible = true;
29694     this.minWidth = config.minWidth || 20;
29695     this.minHeight = config.minHeight || 20;
29696 };
29697
29698 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
29699     hide : function(){
29700         // center panel can't be hidden
29701     },
29702     
29703     show : function(){
29704         // center panel can't be hidden
29705     },
29706     
29707     getMinWidth: function(){
29708         return this.minWidth;
29709     },
29710     
29711     getMinHeight: function(){
29712         return this.minHeight;
29713     }
29714 });
29715
29716
29717 Roo.NorthLayoutRegion = function(mgr, config){
29718     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
29719     if(this.split){
29720         this.split.placement = Roo.SplitBar.TOP;
29721         this.split.orientation = Roo.SplitBar.VERTICAL;
29722         this.split.el.addClass("x-layout-split-v");
29723     }
29724     var size = config.initialSize || config.height;
29725     if(typeof size != "undefined"){
29726         this.el.setHeight(size);
29727     }
29728 };
29729 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
29730     orientation: Roo.SplitBar.VERTICAL,
29731     getBox : function(){
29732         if(this.collapsed){
29733             return this.collapsedEl.getBox();
29734         }
29735         var box = this.el.getBox();
29736         if(this.split){
29737             box.height += this.split.el.getHeight();
29738         }
29739         return box;
29740     },
29741     
29742     updateBox : function(box){
29743         if(this.split && !this.collapsed){
29744             box.height -= this.split.el.getHeight();
29745             this.split.el.setLeft(box.x);
29746             this.split.el.setTop(box.y+box.height);
29747             this.split.el.setWidth(box.width);
29748         }
29749         if(this.collapsed){
29750             this.updateBody(box.width, null);
29751         }
29752         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29753     }
29754 });
29755
29756 Roo.SouthLayoutRegion = function(mgr, config){
29757     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
29758     if(this.split){
29759         this.split.placement = Roo.SplitBar.BOTTOM;
29760         this.split.orientation = Roo.SplitBar.VERTICAL;
29761         this.split.el.addClass("x-layout-split-v");
29762     }
29763     var size = config.initialSize || config.height;
29764     if(typeof size != "undefined"){
29765         this.el.setHeight(size);
29766     }
29767 };
29768 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
29769     orientation: Roo.SplitBar.VERTICAL,
29770     getBox : function(){
29771         if(this.collapsed){
29772             return this.collapsedEl.getBox();
29773         }
29774         var box = this.el.getBox();
29775         if(this.split){
29776             var sh = this.split.el.getHeight();
29777             box.height += sh;
29778             box.y -= sh;
29779         }
29780         return box;
29781     },
29782     
29783     updateBox : function(box){
29784         if(this.split && !this.collapsed){
29785             var sh = this.split.el.getHeight();
29786             box.height -= sh;
29787             box.y += sh;
29788             this.split.el.setLeft(box.x);
29789             this.split.el.setTop(box.y-sh);
29790             this.split.el.setWidth(box.width);
29791         }
29792         if(this.collapsed){
29793             this.updateBody(box.width, null);
29794         }
29795         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29796     }
29797 });
29798
29799 Roo.EastLayoutRegion = function(mgr, config){
29800     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
29801     if(this.split){
29802         this.split.placement = Roo.SplitBar.RIGHT;
29803         this.split.orientation = Roo.SplitBar.HORIZONTAL;
29804         this.split.el.addClass("x-layout-split-h");
29805     }
29806     var size = config.initialSize || config.width;
29807     if(typeof size != "undefined"){
29808         this.el.setWidth(size);
29809     }
29810 };
29811 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
29812     orientation: Roo.SplitBar.HORIZONTAL,
29813     getBox : function(){
29814         if(this.collapsed){
29815             return this.collapsedEl.getBox();
29816         }
29817         var box = this.el.getBox();
29818         if(this.split){
29819             var sw = this.split.el.getWidth();
29820             box.width += sw;
29821             box.x -= sw;
29822         }
29823         return box;
29824     },
29825
29826     updateBox : function(box){
29827         if(this.split && !this.collapsed){
29828             var sw = this.split.el.getWidth();
29829             box.width -= sw;
29830             this.split.el.setLeft(box.x);
29831             this.split.el.setTop(box.y);
29832             this.split.el.setHeight(box.height);
29833             box.x += sw;
29834         }
29835         if(this.collapsed){
29836             this.updateBody(null, box.height);
29837         }
29838         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29839     }
29840 });
29841
29842 Roo.WestLayoutRegion = function(mgr, config){
29843     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
29844     if(this.split){
29845         this.split.placement = Roo.SplitBar.LEFT;
29846         this.split.orientation = Roo.SplitBar.HORIZONTAL;
29847         this.split.el.addClass("x-layout-split-h");
29848     }
29849     var size = config.initialSize || config.width;
29850     if(typeof size != "undefined"){
29851         this.el.setWidth(size);
29852     }
29853 };
29854 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
29855     orientation: Roo.SplitBar.HORIZONTAL,
29856     getBox : function(){
29857         if(this.collapsed){
29858             return this.collapsedEl.getBox();
29859         }
29860         var box = this.el.getBox();
29861         if(this.split){
29862             box.width += this.split.el.getWidth();
29863         }
29864         return box;
29865     },
29866     
29867     updateBox : function(box){
29868         if(this.split && !this.collapsed){
29869             var sw = this.split.el.getWidth();
29870             box.width -= sw;
29871             this.split.el.setLeft(box.x+box.width);
29872             this.split.el.setTop(box.y);
29873             this.split.el.setHeight(box.height);
29874         }
29875         if(this.collapsed){
29876             this.updateBody(null, box.height);
29877         }
29878         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29879     }
29880 });
29881 /*
29882  * Based on:
29883  * Ext JS Library 1.1.1
29884  * Copyright(c) 2006-2007, Ext JS, LLC.
29885  *
29886  * Originally Released Under LGPL - original licence link has changed is not relivant.
29887  *
29888  * Fork - LGPL
29889  * <script type="text/javascript">
29890  */
29891  
29892  
29893 /*
29894  * Private internal class for reading and applying state
29895  */
29896 Roo.LayoutStateManager = function(layout){
29897      // default empty state
29898      this.state = {
29899         north: {},
29900         south: {},
29901         east: {},
29902         west: {}       
29903     };
29904 };
29905
29906 Roo.LayoutStateManager.prototype = {
29907     init : function(layout, provider){
29908         this.provider = provider;
29909         var state = provider.get(layout.id+"-layout-state");
29910         if(state){
29911             var wasUpdating = layout.isUpdating();
29912             if(!wasUpdating){
29913                 layout.beginUpdate();
29914             }
29915             for(var key in state){
29916                 if(typeof state[key] != "function"){
29917                     var rstate = state[key];
29918                     var r = layout.getRegion(key);
29919                     if(r && rstate){
29920                         if(rstate.size){
29921                             r.resizeTo(rstate.size);
29922                         }
29923                         if(rstate.collapsed == true){
29924                             r.collapse(true);
29925                         }else{
29926                             r.expand(null, true);
29927                         }
29928                     }
29929                 }
29930             }
29931             if(!wasUpdating){
29932                 layout.endUpdate();
29933             }
29934             this.state = state; 
29935         }
29936         this.layout = layout;
29937         layout.on("regionresized", this.onRegionResized, this);
29938         layout.on("regioncollapsed", this.onRegionCollapsed, this);
29939         layout.on("regionexpanded", this.onRegionExpanded, this);
29940     },
29941     
29942     storeState : function(){
29943         this.provider.set(this.layout.id+"-layout-state", this.state);
29944     },
29945     
29946     onRegionResized : function(region, newSize){
29947         this.state[region.getPosition()].size = newSize;
29948         this.storeState();
29949     },
29950     
29951     onRegionCollapsed : function(region){
29952         this.state[region.getPosition()].collapsed = true;
29953         this.storeState();
29954     },
29955     
29956     onRegionExpanded : function(region){
29957         this.state[region.getPosition()].collapsed = false;
29958         this.storeState();
29959     }
29960 };/*
29961  * Based on:
29962  * Ext JS Library 1.1.1
29963  * Copyright(c) 2006-2007, Ext JS, LLC.
29964  *
29965  * Originally Released Under LGPL - original licence link has changed is not relivant.
29966  *
29967  * Fork - LGPL
29968  * <script type="text/javascript">
29969  */
29970 /**
29971  * @class Roo.ContentPanel
29972  * @extends Roo.util.Observable
29973  * A basic ContentPanel element.
29974  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
29975  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
29976  * @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
29977  * @cfg {Boolean} closable True if the panel can be closed/removed
29978  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
29979  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
29980  * @cfg {Toolbar} toolbar A toolbar for this panel
29981  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
29982  * @cfg {String} title The title for this panel
29983  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
29984  * @cfg {String} url Calls {@link #setUrl} with this value
29985  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
29986  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
29987  * @constructor
29988  * Create a new ContentPanel.
29989  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
29990  * @param {String/Object} config A string to set only the title or a config object
29991  * @param {String} content (optional) Set the HTML content for this panel
29992  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
29993  */
29994 Roo.ContentPanel = function(el, config, content){
29995     
29996      
29997     /*
29998     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
29999         config = el;
30000         el = Roo.id();
30001     }
30002     if (config && config.parentLayout) { 
30003         el = config.parentLayout.el.createChild(); 
30004     }
30005     */
30006     if(el.autoCreate){ // xtype is available if this is called from factory
30007         config = el;
30008         el = Roo.id();
30009     }
30010     this.el = Roo.get(el);
30011     if(!this.el && config && config.autoCreate){
30012         if(typeof config.autoCreate == "object"){
30013             if(!config.autoCreate.id){
30014                 config.autoCreate.id = config.id||el;
30015             }
30016             this.el = Roo.DomHelper.append(document.body,
30017                         config.autoCreate, true);
30018         }else{
30019             this.el = Roo.DomHelper.append(document.body,
30020                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30021         }
30022     }
30023     this.closable = false;
30024     this.loaded = false;
30025     this.active = false;
30026     if(typeof config == "string"){
30027         this.title = config;
30028     }else{
30029         Roo.apply(this, config);
30030     }
30031     
30032     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30033         this.wrapEl = this.el.wrap();    
30034         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30035         
30036     }
30037     
30038     
30039     
30040     if(this.resizeEl){
30041         this.resizeEl = Roo.get(this.resizeEl, true);
30042     }else{
30043         this.resizeEl = this.el;
30044     }
30045     this.addEvents({
30046         /**
30047          * @event activate
30048          * Fires when this panel is activated. 
30049          * @param {Roo.ContentPanel} this
30050          */
30051         "activate" : true,
30052         /**
30053          * @event deactivate
30054          * Fires when this panel is activated. 
30055          * @param {Roo.ContentPanel} this
30056          */
30057         "deactivate" : true,
30058
30059         /**
30060          * @event resize
30061          * Fires when this panel is resized if fitToFrame is true.
30062          * @param {Roo.ContentPanel} this
30063          * @param {Number} width The width after any component adjustments
30064          * @param {Number} height The height after any component adjustments
30065          */
30066         "resize" : true
30067     });
30068     if(this.autoScroll){
30069         this.resizeEl.setStyle("overflow", "auto");
30070     }
30071     content = content || this.content;
30072     if(content){
30073         this.setContent(content);
30074     }
30075     if(config && config.url){
30076         this.setUrl(this.url, this.params, this.loadOnce);
30077     }
30078     
30079     
30080     
30081     Roo.ContentPanel.superclass.constructor.call(this);
30082 };
30083
30084 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30085     tabTip:'',
30086     setRegion : function(region){
30087         this.region = region;
30088         if(region){
30089            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30090         }else{
30091            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30092         } 
30093     },
30094     
30095     /**
30096      * Returns the toolbar for this Panel if one was configured. 
30097      * @return {Roo.Toolbar} 
30098      */
30099     getToolbar : function(){
30100         return this.toolbar;
30101     },
30102     
30103     setActiveState : function(active){
30104         this.active = active;
30105         if(!active){
30106             this.fireEvent("deactivate", this);
30107         }else{
30108             this.fireEvent("activate", this);
30109         }
30110     },
30111     /**
30112      * Updates this panel's element
30113      * @param {String} content The new content
30114      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30115     */
30116     setContent : function(content, loadScripts){
30117         this.el.update(content, loadScripts);
30118     },
30119
30120     ignoreResize : function(w, h){
30121         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30122             return true;
30123         }else{
30124             this.lastSize = {width: w, height: h};
30125             return false;
30126         }
30127     },
30128     /**
30129      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30130      * @return {Roo.UpdateManager} The UpdateManager
30131      */
30132     getUpdateManager : function(){
30133         return this.el.getUpdateManager();
30134     },
30135      /**
30136      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30137      * @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:
30138 <pre><code>
30139 panel.load({
30140     url: "your-url.php",
30141     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30142     callback: yourFunction,
30143     scope: yourObject, //(optional scope)
30144     discardUrl: false,
30145     nocache: false,
30146     text: "Loading...",
30147     timeout: 30,
30148     scripts: false
30149 });
30150 </code></pre>
30151      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30152      * 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.
30153      * @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}
30154      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30155      * @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.
30156      * @return {Roo.ContentPanel} this
30157      */
30158     load : function(){
30159         var um = this.el.getUpdateManager();
30160         um.update.apply(um, arguments);
30161         return this;
30162     },
30163
30164
30165     /**
30166      * 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.
30167      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
30168      * @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)
30169      * @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)
30170      * @return {Roo.UpdateManager} The UpdateManager
30171      */
30172     setUrl : function(url, params, loadOnce){
30173         if(this.refreshDelegate){
30174             this.removeListener("activate", this.refreshDelegate);
30175         }
30176         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30177         this.on("activate", this.refreshDelegate);
30178         return this.el.getUpdateManager();
30179     },
30180     
30181     _handleRefresh : function(url, params, loadOnce){
30182         if(!loadOnce || !this.loaded){
30183             var updater = this.el.getUpdateManager();
30184             updater.update(url, params, this._setLoaded.createDelegate(this));
30185         }
30186     },
30187     
30188     _setLoaded : function(){
30189         this.loaded = true;
30190     }, 
30191     
30192     /**
30193      * Returns this panel's id
30194      * @return {String} 
30195      */
30196     getId : function(){
30197         return this.el.id;
30198     },
30199     
30200     /** 
30201      * Returns this panel's element - used by regiosn to add.
30202      * @return {Roo.Element} 
30203      */
30204     getEl : function(){
30205         return this.wrapEl || this.el;
30206     },
30207     
30208     adjustForComponents : function(width, height){
30209         if(this.resizeEl != this.el){
30210             width -= this.el.getFrameWidth('lr');
30211             height -= this.el.getFrameWidth('tb');
30212         }
30213         if(this.toolbar){
30214             var te = this.toolbar.getEl();
30215             height -= te.getHeight();
30216             te.setWidth(width);
30217         }
30218         if(this.adjustments){
30219             width += this.adjustments[0];
30220             height += this.adjustments[1];
30221         }
30222         return {"width": width, "height": height};
30223     },
30224     
30225     setSize : function(width, height){
30226         if(this.fitToFrame && !this.ignoreResize(width, height)){
30227             if(this.fitContainer && this.resizeEl != this.el){
30228                 this.el.setSize(width, height);
30229             }
30230             var size = this.adjustForComponents(width, height);
30231             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
30232             this.fireEvent('resize', this, size.width, size.height);
30233         }
30234     },
30235     
30236     /**
30237      * Returns this panel's title
30238      * @return {String} 
30239      */
30240     getTitle : function(){
30241         return this.title;
30242     },
30243     
30244     /**
30245      * Set this panel's title
30246      * @param {String} title
30247      */
30248     setTitle : function(title){
30249         this.title = title;
30250         if(this.region){
30251             this.region.updatePanelTitle(this, title);
30252         }
30253     },
30254     
30255     /**
30256      * Returns true is this panel was configured to be closable
30257      * @return {Boolean} 
30258      */
30259     isClosable : function(){
30260         return this.closable;
30261     },
30262     
30263     beforeSlide : function(){
30264         this.el.clip();
30265         this.resizeEl.clip();
30266     },
30267     
30268     afterSlide : function(){
30269         this.el.unclip();
30270         this.resizeEl.unclip();
30271     },
30272     
30273     /**
30274      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
30275      *   Will fail silently if the {@link #setUrl} method has not been called.
30276      *   This does not activate the panel, just updates its content.
30277      */
30278     refresh : function(){
30279         if(this.refreshDelegate){
30280            this.loaded = false;
30281            this.refreshDelegate();
30282         }
30283     },
30284     
30285     /**
30286      * Destroys this panel
30287      */
30288     destroy : function(){
30289         this.el.removeAllListeners();
30290         var tempEl = document.createElement("span");
30291         tempEl.appendChild(this.el.dom);
30292         tempEl.innerHTML = "";
30293         this.el.remove();
30294         this.el = null;
30295     },
30296     
30297       /**
30298      * Adds a xtype elements to the panel - currently only supports Forms.
30299      * <pre><code>
30300
30301 layout.addxtype({
30302        xtype : 'Form',
30303        items: [ .... ]
30304    }
30305 );
30306
30307 </code></pre>
30308      * @param {Object} cfg Xtype definition of item to add.
30309      */
30310     
30311     addxtype : function(cfg) {
30312         // add form..
30313         if (!cfg.xtype.match(/^Form$/)) {
30314             return false;
30315         }
30316         var el = this.el.createChild();
30317
30318         this.form = new  Roo.form.Form(cfg);
30319         
30320         
30321         if ( this.form.allItems.length) this.form.render(el.dom);
30322         return this.form;
30323         
30324     }
30325 });
30326
30327 /**
30328  * @class Roo.GridPanel
30329  * @extends Roo.ContentPanel
30330  * @constructor
30331  * Create a new GridPanel.
30332  * @param {Roo.grid.Grid} grid The grid for this panel
30333  * @param {String/Object} config A string to set only the panel's title, or a config object
30334  */
30335 Roo.GridPanel = function(grid, config){
30336     
30337   
30338     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
30339         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
30340         
30341     this.wrapper.dom.appendChild(grid.getGridEl().dom);
30342     
30343     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
30344     
30345     if(this.toolbar){
30346         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
30347     }
30348     // xtype created footer. - not sure if will work as we normally have to render first..
30349     if (this.footer && !this.footer.el && this.footer.xtype) {
30350         
30351         this.footer.container = this.grid.getView().getFooterPanel(true);
30352         this.footer.dataSource = this.grid.dataSource;
30353         this.footer = Roo.factory(this.footer, Roo);
30354         
30355     }
30356     
30357     grid.monitorWindowResize = false; // turn off autosizing
30358     grid.autoHeight = false;
30359     grid.autoWidth = false;
30360     this.grid = grid;
30361     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
30362 };
30363
30364 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
30365     getId : function(){
30366         return this.grid.id;
30367     },
30368     
30369     /**
30370      * Returns the grid for this panel
30371      * @return {Roo.grid.Grid} 
30372      */
30373     getGrid : function(){
30374         return this.grid;    
30375     },
30376     
30377     setSize : function(width, height){
30378         if(!this.ignoreResize(width, height)){
30379             var grid = this.grid;
30380             var size = this.adjustForComponents(width, height);
30381             grid.getGridEl().setSize(size.width, size.height);
30382             grid.autoSize();
30383         }
30384     },
30385     
30386     beforeSlide : function(){
30387         this.grid.getView().scroller.clip();
30388     },
30389     
30390     afterSlide : function(){
30391         this.grid.getView().scroller.unclip();
30392     },
30393     
30394     destroy : function(){
30395         this.grid.destroy();
30396         delete this.grid;
30397         Roo.GridPanel.superclass.destroy.call(this); 
30398     }
30399 });
30400
30401
30402 /**
30403  * @class Roo.NestedLayoutPanel
30404  * @extends Roo.ContentPanel
30405  * @constructor
30406  * Create a new NestedLayoutPanel.
30407  * 
30408  * 
30409  * @param {Roo.BorderLayout} layout The layout for this panel
30410  * @param {String/Object} config A string to set only the title or a config object
30411  */
30412 Roo.NestedLayoutPanel = function(layout, config)
30413 {
30414     // construct with only one argument..
30415     /* FIXME - implement nicer consturctors
30416     if (layout.layout) {
30417         config = layout;
30418         layout = config.layout;
30419         delete config.layout;
30420     }
30421     if (layout.xtype && !layout.getEl) {
30422         // then layout needs constructing..
30423         layout = Roo.factory(layout, Roo);
30424     }
30425     */
30426     
30427     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
30428     
30429     layout.monitorWindowResize = false; // turn off autosizing
30430     this.layout = layout;
30431     this.layout.getEl().addClass("x-layout-nested-layout");
30432     
30433     
30434     
30435 };
30436
30437 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
30438
30439     setSize : function(width, height){
30440         if(!this.ignoreResize(width, height)){
30441             var size = this.adjustForComponents(width, height);
30442             var el = this.layout.getEl();
30443             el.setSize(size.width, size.height);
30444             var touch = el.dom.offsetWidth;
30445             this.layout.layout();
30446             // ie requires a double layout on the first pass
30447             if(Roo.isIE && !this.initialized){
30448                 this.initialized = true;
30449                 this.layout.layout();
30450             }
30451         }
30452     },
30453     
30454     // activate all subpanels if not currently active..
30455     
30456     setActiveState : function(active){
30457         this.active = active;
30458         if(!active){
30459             this.fireEvent("deactivate", this);
30460             return;
30461         }
30462         
30463         this.fireEvent("activate", this);
30464         // not sure if this should happen before or after..
30465         if (!this.layout) {
30466             return; // should not happen..
30467         }
30468         var reg = false;
30469         for (var r in this.layout.regions) {
30470             reg = this.layout.getRegion(r);
30471             if (reg.getActivePanel()) {
30472                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
30473                 reg.setActivePanel(reg.getActivePanel());
30474                 continue;
30475             }
30476             if (!reg.panels.length) {
30477                 continue;
30478             }
30479             reg.showPanel(reg.getPanel(0));
30480         }
30481         
30482         
30483         
30484         
30485     },
30486     
30487     /**
30488      * Returns the nested BorderLayout for this panel
30489      * @return {Roo.BorderLayout} 
30490      */
30491     getLayout : function(){
30492         return this.layout;
30493     },
30494     
30495      /**
30496      * Adds a xtype elements to the layout of the nested panel
30497      * <pre><code>
30498
30499 panel.addxtype({
30500        xtype : 'ContentPanel',
30501        region: 'west',
30502        items: [ .... ]
30503    }
30504 );
30505
30506 panel.addxtype({
30507         xtype : 'NestedLayoutPanel',
30508         region: 'west',
30509         layout: {
30510            center: { },
30511            west: { }   
30512         },
30513         items : [ ... list of content panels or nested layout panels.. ]
30514    }
30515 );
30516 </code></pre>
30517      * @param {Object} cfg Xtype definition of item to add.
30518      */
30519     addxtype : function(cfg) {
30520         return this.layout.addxtype(cfg);
30521     
30522     }
30523 });
30524
30525 Roo.ScrollPanel = function(el, config, content){
30526     config = config || {};
30527     config.fitToFrame = true;
30528     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
30529     
30530     this.el.dom.style.overflow = "hidden";
30531     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
30532     this.el.removeClass("x-layout-inactive-content");
30533     this.el.on("mousewheel", this.onWheel, this);
30534
30535     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
30536     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
30537     up.unselectable(); down.unselectable();
30538     up.on("click", this.scrollUp, this);
30539     down.on("click", this.scrollDown, this);
30540     up.addClassOnOver("x-scroller-btn-over");
30541     down.addClassOnOver("x-scroller-btn-over");
30542     up.addClassOnClick("x-scroller-btn-click");
30543     down.addClassOnClick("x-scroller-btn-click");
30544     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
30545
30546     this.resizeEl = this.el;
30547     this.el = wrap; this.up = up; this.down = down;
30548 };
30549
30550 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
30551     increment : 100,
30552     wheelIncrement : 5,
30553     scrollUp : function(){
30554         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
30555     },
30556
30557     scrollDown : function(){
30558         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
30559     },
30560
30561     afterScroll : function(){
30562         var el = this.resizeEl;
30563         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
30564         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
30565         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
30566     },
30567
30568     setSize : function(){
30569         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
30570         this.afterScroll();
30571     },
30572
30573     onWheel : function(e){
30574         var d = e.getWheelDelta();
30575         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
30576         this.afterScroll();
30577         e.stopEvent();
30578     },
30579
30580     setContent : function(content, loadScripts){
30581         this.resizeEl.update(content, loadScripts);
30582     }
30583
30584 });
30585
30586
30587
30588
30589
30590
30591
30592
30593
30594 /**
30595  * @class Roo.TreePanel
30596  * @extends Roo.ContentPanel
30597  * @constructor
30598  * Create a new TreePanel.
30599  * @param {String/Object} config A string to set only the panel's title, or a config object
30600  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
30601  */
30602 Roo.TreePanel = function(config){
30603     var el = config.el;
30604     var tree = config.tree;
30605     delete config.tree; 
30606     delete config.el; // hopefull!
30607     Roo.TreePanel.superclass.constructor.call(this, el, config);
30608     var treeEl = el.createChild();
30609     this.tree = new Roo.tree.TreePanel(treeEl , tree);
30610     //console.log(tree);
30611     this.on('activate', function()
30612     {
30613         if (this.tree.rendered) {
30614             return;
30615         }
30616         //console.log('render tree');
30617         this.tree.render();
30618     });
30619     
30620     this.on('resize',  function (cp, w, h) {
30621             this.tree.innerCt.setWidth(w);
30622             this.tree.innerCt.setHeight(h);
30623             this.tree.innerCt.setStyle('overflow-y', 'auto');
30624     });
30625
30626         
30627     
30628 };
30629
30630 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
30631
30632
30633
30634
30635
30636
30637
30638
30639
30640
30641
30642 /*
30643  * Based on:
30644  * Ext JS Library 1.1.1
30645  * Copyright(c) 2006-2007, Ext JS, LLC.
30646  *
30647  * Originally Released Under LGPL - original licence link has changed is not relivant.
30648  *
30649  * Fork - LGPL
30650  * <script type="text/javascript">
30651  */
30652  
30653
30654 /**
30655  * @class Roo.ReaderLayout
30656  * @extends Roo.BorderLayout
30657  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
30658  * center region containing two nested regions (a top one for a list view and one for item preview below),
30659  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
30660  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
30661  * expedites the setup of the overall layout and regions for this common application style.
30662  * Example:
30663  <pre><code>
30664 var reader = new Roo.ReaderLayout();
30665 var CP = Roo.ContentPanel;  // shortcut for adding
30666
30667 reader.beginUpdate();
30668 reader.add("north", new CP("north", "North"));
30669 reader.add("west", new CP("west", {title: "West"}));
30670 reader.add("east", new CP("east", {title: "East"}));
30671
30672 reader.regions.listView.add(new CP("listView", "List"));
30673 reader.regions.preview.add(new CP("preview", "Preview"));
30674 reader.endUpdate();
30675 </code></pre>
30676 * @constructor
30677 * Create a new ReaderLayout
30678 * @param {Object} config Configuration options
30679 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
30680 * document.body if omitted)
30681 */
30682 Roo.ReaderLayout = function(config, renderTo){
30683     var c = config || {size:{}};
30684     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
30685         north: c.north !== false ? Roo.apply({
30686             split:false,
30687             initialSize: 32,
30688             titlebar: false
30689         }, c.north) : false,
30690         west: c.west !== false ? Roo.apply({
30691             split:true,
30692             initialSize: 200,
30693             minSize: 175,
30694             maxSize: 400,
30695             titlebar: true,
30696             collapsible: true,
30697             animate: true,
30698             margins:{left:5,right:0,bottom:5,top:5},
30699             cmargins:{left:5,right:5,bottom:5,top:5}
30700         }, c.west) : false,
30701         east: c.east !== false ? Roo.apply({
30702             split:true,
30703             initialSize: 200,
30704             minSize: 175,
30705             maxSize: 400,
30706             titlebar: true,
30707             collapsible: true,
30708             animate: true,
30709             margins:{left:0,right:5,bottom:5,top:5},
30710             cmargins:{left:5,right:5,bottom:5,top:5}
30711         }, c.east) : false,
30712         center: Roo.apply({
30713             tabPosition: 'top',
30714             autoScroll:false,
30715             closeOnTab: true,
30716             titlebar:false,
30717             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
30718         }, c.center)
30719     });
30720
30721     this.el.addClass('x-reader');
30722
30723     this.beginUpdate();
30724
30725     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
30726         south: c.preview !== false ? Roo.apply({
30727             split:true,
30728             initialSize: 200,
30729             minSize: 100,
30730             autoScroll:true,
30731             collapsible:true,
30732             titlebar: true,
30733             cmargins:{top:5,left:0, right:0, bottom:0}
30734         }, c.preview) : false,
30735         center: Roo.apply({
30736             autoScroll:false,
30737             titlebar:false,
30738             minHeight:200
30739         }, c.listView)
30740     });
30741     this.add('center', new Roo.NestedLayoutPanel(inner,
30742             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
30743
30744     this.endUpdate();
30745
30746     this.regions.preview = inner.getRegion('south');
30747     this.regions.listView = inner.getRegion('center');
30748 };
30749
30750 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
30751  * Based on:
30752  * Ext JS Library 1.1.1
30753  * Copyright(c) 2006-2007, Ext JS, LLC.
30754  *
30755  * Originally Released Under LGPL - original licence link has changed is not relivant.
30756  *
30757  * Fork - LGPL
30758  * <script type="text/javascript">
30759  */
30760  
30761 /**
30762  * @class Roo.grid.Grid
30763  * @extends Roo.util.Observable
30764  * This class represents the primary interface of a component based grid control.
30765  * <br><br>Usage:<pre><code>
30766  var grid = new Roo.grid.Grid("my-container-id", {
30767      ds: myDataStore,
30768      cm: myColModel,
30769      selModel: mySelectionModel,
30770      autoSizeColumns: true,
30771      monitorWindowResize: false,
30772      trackMouseOver: true
30773  });
30774  // set any options
30775  grid.render();
30776  * </code></pre>
30777  * <b>Common Problems:</b><br/>
30778  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
30779  * element will correct this<br/>
30780  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
30781  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
30782  * are unpredictable.<br/>
30783  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
30784  * grid to calculate dimensions/offsets.<br/>
30785   * @constructor
30786  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
30787  * The container MUST have some type of size defined for the grid to fill. The container will be
30788  * automatically set to position relative if it isn't already.
30789  * @param {Object} config A config object that sets properties on this grid.
30790  */
30791 Roo.grid.Grid = function(container, config){
30792         // initialize the container
30793         this.container = Roo.get(container);
30794         this.container.update("");
30795         this.container.setStyle("overflow", "hidden");
30796     this.container.addClass('x-grid-container');
30797
30798     this.id = this.container.id;
30799
30800     Roo.apply(this, config);
30801     // check and correct shorthanded configs
30802     if(this.ds){
30803         this.dataSource = this.ds;
30804         delete this.ds;
30805     }
30806     if(this.cm){
30807         this.colModel = this.cm;
30808         delete this.cm;
30809     }
30810     if(this.sm){
30811         this.selModel = this.sm;
30812         delete this.sm;
30813     }
30814
30815     if (this.selModel) {
30816         this.selModel = Roo.factory(this.selModel, Roo.grid);
30817         this.sm = this.selModel;
30818         this.sm.xmodule = this.xmodule || false;
30819     }
30820     if (typeof(this.colModel.config) == 'undefined') {
30821         this.colModel = new Roo.grid.ColumnModel(this.colModel);
30822         this.cm = this.colModel;
30823         this.cm.xmodule = this.xmodule || false;
30824     }
30825     if (this.dataSource) {
30826         this.dataSource= Roo.factory(this.dataSource, Roo.data);
30827         this.ds = this.dataSource;
30828         this.ds.xmodule = this.xmodule || false;
30829         
30830     }
30831     
30832     
30833     
30834     if(this.width){
30835         this.container.setWidth(this.width);
30836     }
30837
30838     if(this.height){
30839         this.container.setHeight(this.height);
30840     }
30841     /** @private */
30842         this.addEvents({
30843             // raw events
30844             /**
30845              * @event click
30846              * The raw click event for the entire grid.
30847              * @param {Roo.EventObject} e
30848              */
30849             "click" : true,
30850             /**
30851              * @event dblclick
30852              * The raw dblclick event for the entire grid.
30853              * @param {Roo.EventObject} e
30854              */
30855             "dblclick" : true,
30856             /**
30857              * @event contextmenu
30858              * The raw contextmenu event for the entire grid.
30859              * @param {Roo.EventObject} e
30860              */
30861             "contextmenu" : true,
30862             /**
30863              * @event mousedown
30864              * The raw mousedown event for the entire grid.
30865              * @param {Roo.EventObject} e
30866              */
30867             "mousedown" : true,
30868             /**
30869              * @event mouseup
30870              * The raw mouseup event for the entire grid.
30871              * @param {Roo.EventObject} e
30872              */
30873             "mouseup" : true,
30874             /**
30875              * @event mouseover
30876              * The raw mouseover event for the entire grid.
30877              * @param {Roo.EventObject} e
30878              */
30879             "mouseover" : true,
30880             /**
30881              * @event mouseout
30882              * The raw mouseout event for the entire grid.
30883              * @param {Roo.EventObject} e
30884              */
30885             "mouseout" : true,
30886             /**
30887              * @event keypress
30888              * The raw keypress event for the entire grid.
30889              * @param {Roo.EventObject} e
30890              */
30891             "keypress" : true,
30892             /**
30893              * @event keydown
30894              * The raw keydown event for the entire grid.
30895              * @param {Roo.EventObject} e
30896              */
30897             "keydown" : true,
30898
30899             // custom events
30900
30901             /**
30902              * @event cellclick
30903              * Fires when a cell is clicked
30904              * @param {Grid} this
30905              * @param {Number} rowIndex
30906              * @param {Number} columnIndex
30907              * @param {Roo.EventObject} e
30908              */
30909             "cellclick" : true,
30910             /**
30911              * @event celldblclick
30912              * Fires when a cell is double clicked
30913              * @param {Grid} this
30914              * @param {Number} rowIndex
30915              * @param {Number} columnIndex
30916              * @param {Roo.EventObject} e
30917              */
30918             "celldblclick" : true,
30919             /**
30920              * @event rowclick
30921              * Fires when a row is clicked
30922              * @param {Grid} this
30923              * @param {Number} rowIndex
30924              * @param {Roo.EventObject} e
30925              */
30926             "rowclick" : true,
30927             /**
30928              * @event rowdblclick
30929              * Fires when a row is double clicked
30930              * @param {Grid} this
30931              * @param {Number} rowIndex
30932              * @param {Roo.EventObject} e
30933              */
30934             "rowdblclick" : true,
30935             /**
30936              * @event headerclick
30937              * Fires when a header is clicked
30938              * @param {Grid} this
30939              * @param {Number} columnIndex
30940              * @param {Roo.EventObject} e
30941              */
30942             "headerclick" : true,
30943             /**
30944              * @event headerdblclick
30945              * Fires when a header cell is double clicked
30946              * @param {Grid} this
30947              * @param {Number} columnIndex
30948              * @param {Roo.EventObject} e
30949              */
30950             "headerdblclick" : true,
30951             /**
30952              * @event rowcontextmenu
30953              * Fires when a row is right clicked
30954              * @param {Grid} this
30955              * @param {Number} rowIndex
30956              * @param {Roo.EventObject} e
30957              */
30958             "rowcontextmenu" : true,
30959             /**
30960          * @event cellcontextmenu
30961          * Fires when a cell is right clicked
30962          * @param {Grid} this
30963          * @param {Number} rowIndex
30964          * @param {Number} cellIndex
30965          * @param {Roo.EventObject} e
30966          */
30967          "cellcontextmenu" : true,
30968             /**
30969              * @event headercontextmenu
30970              * Fires when a header is right clicked
30971              * @param {Grid} this
30972              * @param {Number} columnIndex
30973              * @param {Roo.EventObject} e
30974              */
30975             "headercontextmenu" : true,
30976             /**
30977              * @event bodyscroll
30978              * Fires when the body element is scrolled
30979              * @param {Number} scrollLeft
30980              * @param {Number} scrollTop
30981              */
30982             "bodyscroll" : true,
30983             /**
30984              * @event columnresize
30985              * Fires when the user resizes a column
30986              * @param {Number} columnIndex
30987              * @param {Number} newSize
30988              */
30989             "columnresize" : true,
30990             /**
30991              * @event columnmove
30992              * Fires when the user moves a column
30993              * @param {Number} oldIndex
30994              * @param {Number} newIndex
30995              */
30996             "columnmove" : true,
30997             /**
30998              * @event startdrag
30999              * Fires when row(s) start being dragged
31000              * @param {Grid} this
31001              * @param {Roo.GridDD} dd The drag drop object
31002              * @param {event} e The raw browser event
31003              */
31004             "startdrag" : true,
31005             /**
31006              * @event enddrag
31007              * Fires when a drag operation is complete
31008              * @param {Grid} this
31009              * @param {Roo.GridDD} dd The drag drop object
31010              * @param {event} e The raw browser event
31011              */
31012             "enddrag" : true,
31013             /**
31014              * @event dragdrop
31015              * Fires when dragged row(s) are dropped on a valid DD target
31016              * @param {Grid} this
31017              * @param {Roo.GridDD} dd The drag drop object
31018              * @param {String} targetId The target drag drop object
31019              * @param {event} e The raw browser event
31020              */
31021             "dragdrop" : true,
31022             /**
31023              * @event dragover
31024              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31025              * @param {Grid} this
31026              * @param {Roo.GridDD} dd The drag drop object
31027              * @param {String} targetId The target drag drop object
31028              * @param {event} e The raw browser event
31029              */
31030             "dragover" : true,
31031             /**
31032              * @event dragenter
31033              *  Fires when the dragged row(s) first cross another DD target while being dragged
31034              * @param {Grid} this
31035              * @param {Roo.GridDD} dd The drag drop object
31036              * @param {String} targetId The target drag drop object
31037              * @param {event} e The raw browser event
31038              */
31039             "dragenter" : true,
31040             /**
31041              * @event dragout
31042              * Fires when the dragged row(s) leave another DD target while being dragged
31043              * @param {Grid} this
31044              * @param {Roo.GridDD} dd The drag drop object
31045              * @param {String} targetId The target drag drop object
31046              * @param {event} e The raw browser event
31047              */
31048             "dragout" : true,
31049         /**
31050          * @event render
31051          * Fires when the grid is rendered
31052          * @param {Grid} grid
31053          */
31054         render : true
31055     });
31056
31057     Roo.grid.Grid.superclass.constructor.call(this);
31058 };
31059 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31060     /**
31061      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31062          */
31063         minColumnWidth : 25,
31064
31065     /**
31066          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31067          * <b>on initial render.</b> It is more efficient to explicitly size the columns
31068          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31069          */
31070         autoSizeColumns : false,
31071
31072         /**
31073          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31074          */
31075         autoSizeHeaders : true,
31076
31077         /**
31078          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31079          */
31080         monitorWindowResize : true,
31081
31082         /**
31083          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31084          * rows measured to get a columns size. Default is 0 (all rows).
31085          */
31086         maxRowsToMeasure : 0,
31087
31088         /**
31089          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31090          */
31091         trackMouseOver : true,
31092
31093         /**
31094          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
31095          */
31096         enableDragDrop : false,
31097
31098         /**
31099          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
31100          */
31101         enableColumnMove : true,
31102
31103         /**
31104          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
31105          */
31106         enableColumnHide : true,
31107
31108         /**
31109          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
31110          */
31111         enableRowHeightSync : false,
31112
31113         /**
31114          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
31115          */
31116         stripeRows : true,
31117
31118         /**
31119          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
31120          */
31121         autoHeight : false,
31122
31123     /**
31124      * @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.
31125      */
31126     autoExpandColumn : false,
31127
31128     /**
31129     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
31130     * Default is 50.
31131     */
31132     autoExpandMin : 50,
31133
31134     /**
31135     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
31136     */
31137     autoExpandMax : 1000,
31138
31139     /**
31140          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
31141          */
31142         view : null,
31143
31144         /**
31145      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
31146          */
31147         loadMask : false,
31148
31149     // private
31150     rendered : false,
31151
31152     /**
31153     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
31154     * of a fixed width. Default is false.
31155     */
31156     /**
31157     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
31158     */
31159     /**
31160      * Called once after all setup has been completed and the grid is ready to be rendered.
31161      * @return {Roo.grid.Grid} this
31162      */
31163     render : function(){
31164         var c = this.container;
31165         // try to detect autoHeight/width mode
31166         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
31167             this.autoHeight = true;
31168         }
31169         var view = this.getView();
31170         view.init(this);
31171
31172         c.on("click", this.onClick, this);
31173         c.on("dblclick", this.onDblClick, this);
31174         c.on("contextmenu", this.onContextMenu, this);
31175         c.on("keydown", this.onKeyDown, this);
31176
31177         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
31178
31179         this.getSelectionModel().init(this);
31180
31181         view.render();
31182
31183         if(this.loadMask){
31184             this.loadMask = new Roo.LoadMask(this.container,
31185                     Roo.apply({store:this.dataSource}, this.loadMask));
31186         }
31187         
31188         
31189         if (this.toolbar && this.toolbar.xtype) {
31190             this.toolbar.container = this.getView().getHeaderPanel(true);
31191             this.toolbar = new Ext.Toolbar(this.toolbar);
31192         }
31193         if (this.footer && this.footer.xtype) {
31194             this.footer.dataSource = this.getDataSource();
31195             this.footer.container = this.getView().getFooterPanel(true);
31196             this.footer = Roo.factory(this.footer, Roo);
31197         }
31198         this.rendered = true;
31199         this.fireEvent('render', this);
31200         return this;
31201     },
31202
31203         /**
31204          * Reconfigures the grid to use a different Store and Column Model.
31205          * The View will be bound to the new objects and refreshed.
31206          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
31207          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
31208          */
31209     reconfigure : function(dataSource, colModel){
31210         if(this.loadMask){
31211             this.loadMask.destroy();
31212             this.loadMask = new Roo.LoadMask(this.container,
31213                     Roo.apply({store:dataSource}, this.loadMask));
31214         }
31215         this.view.bind(dataSource, colModel);
31216         this.dataSource = dataSource;
31217         this.colModel = colModel;
31218         this.view.refresh(true);
31219     },
31220
31221     // private
31222     onKeyDown : function(e){
31223         this.fireEvent("keydown", e);
31224     },
31225
31226     /**
31227      * Destroy this grid.
31228      * @param {Boolean} removeEl True to remove the element
31229      */
31230     destroy : function(removeEl, keepListeners){
31231         if(this.loadMask){
31232             this.loadMask.destroy();
31233         }
31234         var c = this.container;
31235         c.removeAllListeners();
31236         this.view.destroy();
31237         this.colModel.purgeListeners();
31238         if(!keepListeners){
31239             this.purgeListeners();
31240         }
31241         c.update("");
31242         if(removeEl === true){
31243             c.remove();
31244         }
31245     },
31246
31247     // private
31248     processEvent : function(name, e){
31249         this.fireEvent(name, e);
31250         var t = e.getTarget();
31251         var v = this.view;
31252         var header = v.findHeaderIndex(t);
31253         if(header !== false){
31254             this.fireEvent("header" + name, this, header, e);
31255         }else{
31256             var row = v.findRowIndex(t);
31257             var cell = v.findCellIndex(t);
31258             if(row !== false){
31259                 this.fireEvent("row" + name, this, row, e);
31260                 if(cell !== false){
31261                     this.fireEvent("cell" + name, this, row, cell, e);
31262                 }
31263             }
31264         }
31265     },
31266
31267     // private
31268     onClick : function(e){
31269         this.processEvent("click", e);
31270     },
31271
31272     // private
31273     onContextMenu : function(e, t){
31274         this.processEvent("contextmenu", e);
31275     },
31276
31277     // private
31278     onDblClick : function(e){
31279         this.processEvent("dblclick", e);
31280     },
31281
31282     // private
31283     walkCells : function(row, col, step, fn, scope){
31284         var cm = this.colModel, clen = cm.getColumnCount();
31285         var ds = this.dataSource, rlen = ds.getCount(), first = true;
31286         if(step < 0){
31287             if(col < 0){
31288                 row--;
31289                 first = false;
31290             }
31291             while(row >= 0){
31292                 if(!first){
31293                     col = clen-1;
31294                 }
31295                 first = false;
31296                 while(col >= 0){
31297                     if(fn.call(scope || this, row, col, cm) === true){
31298                         return [row, col];
31299                     }
31300                     col--;
31301                 }
31302                 row--;
31303             }
31304         } else {
31305             if(col >= clen){
31306                 row++;
31307                 first = false;
31308             }
31309             while(row < rlen){
31310                 if(!first){
31311                     col = 0;
31312                 }
31313                 first = false;
31314                 while(col < clen){
31315                     if(fn.call(scope || this, row, col, cm) === true){
31316                         return [row, col];
31317                     }
31318                     col++;
31319                 }
31320                 row++;
31321             }
31322         }
31323         return null;
31324     },
31325
31326     // private
31327     getSelections : function(){
31328         return this.selModel.getSelections();
31329     },
31330
31331     /**
31332      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
31333      * but if manual update is required this method will initiate it.
31334      */
31335     autoSize : function(){
31336         if(this.rendered){
31337             this.view.layout();
31338             if(this.view.adjustForScroll){
31339                 this.view.adjustForScroll();
31340             }
31341         }
31342     },
31343
31344     /**
31345      * Returns the grid's underlying element.
31346      * @return {Element} The element
31347      */
31348     getGridEl : function(){
31349         return this.container;
31350     },
31351
31352     // private for compatibility, overridden by editor grid
31353     stopEditing : function(){},
31354
31355     /**
31356      * Returns the grid's SelectionModel.
31357      * @return {SelectionModel}
31358      */
31359     getSelectionModel : function(){
31360         if(!this.selModel){
31361             this.selModel = new Roo.grid.RowSelectionModel();
31362         }
31363         return this.selModel;
31364     },
31365
31366     /**
31367      * Returns the grid's DataSource.
31368      * @return {DataSource}
31369      */
31370     getDataSource : function(){
31371         return this.dataSource;
31372     },
31373
31374     /**
31375      * Returns the grid's ColumnModel.
31376      * @return {ColumnModel}
31377      */
31378     getColumnModel : function(){
31379         return this.colModel;
31380     },
31381
31382     /**
31383      * Returns the grid's GridView object.
31384      * @return {GridView}
31385      */
31386     getView : function(){
31387         if(!this.view){
31388             this.view = new Roo.grid.GridView(this.viewConfig);
31389         }
31390         return this.view;
31391     },
31392     /**
31393      * Called to get grid's drag proxy text, by default returns this.ddText.
31394      * @return {String}
31395      */
31396     getDragDropText : function(){
31397         var count = this.selModel.getCount();
31398         return String.format(this.ddText, count, count == 1 ? '' : 's');
31399     }
31400 });
31401 /**
31402  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
31403  * %0 is replaced with the number of selected rows.
31404  * @type String
31405  */
31406 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
31407  * Based on:
31408  * Ext JS Library 1.1.1
31409  * Copyright(c) 2006-2007, Ext JS, LLC.
31410  *
31411  * Originally Released Under LGPL - original licence link has changed is not relivant.
31412  *
31413  * Fork - LGPL
31414  * <script type="text/javascript">
31415  */
31416  
31417 Roo.grid.AbstractGridView = function(){
31418         this.grid = null;
31419         
31420         this.events = {
31421             "beforerowremoved" : true,
31422             "beforerowsinserted" : true,
31423             "beforerefresh" : true,
31424             "rowremoved" : true,
31425             "rowsinserted" : true,
31426             "rowupdated" : true,
31427             "refresh" : true
31428         };
31429     Roo.grid.AbstractGridView.superclass.constructor.call(this);
31430 };
31431
31432 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
31433     rowClass : "x-grid-row",
31434     cellClass : "x-grid-cell",
31435     tdClass : "x-grid-td",
31436     hdClass : "x-grid-hd",
31437     splitClass : "x-grid-hd-split",
31438     
31439         init: function(grid){
31440         this.grid = grid;
31441                 var cid = this.grid.getGridEl().id;
31442         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
31443         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
31444         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
31445         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
31446         },
31447         
31448         getColumnRenderers : function(){
31449         var renderers = [];
31450         var cm = this.grid.colModel;
31451         var colCount = cm.getColumnCount();
31452         for(var i = 0; i < colCount; i++){
31453             renderers[i] = cm.getRenderer(i);
31454         }
31455         return renderers;
31456     },
31457     
31458     getColumnIds : function(){
31459         var ids = [];
31460         var cm = this.grid.colModel;
31461         var colCount = cm.getColumnCount();
31462         for(var i = 0; i < colCount; i++){
31463             ids[i] = cm.getColumnId(i);
31464         }
31465         return ids;
31466     },
31467     
31468     getDataIndexes : function(){
31469         if(!this.indexMap){
31470             this.indexMap = this.buildIndexMap();
31471         }
31472         return this.indexMap.colToData;
31473     },
31474     
31475     getColumnIndexByDataIndex : function(dataIndex){
31476         if(!this.indexMap){
31477             this.indexMap = this.buildIndexMap();
31478         }
31479         return this.indexMap.dataToCol[dataIndex];
31480     },
31481     
31482     /**
31483      * Set a css style for a column dynamically. 
31484      * @param {Number} colIndex The index of the column
31485      * @param {String} name The css property name
31486      * @param {String} value The css value
31487      */
31488     setCSSStyle : function(colIndex, name, value){
31489         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
31490         Roo.util.CSS.updateRule(selector, name, value);
31491     },
31492     
31493     generateRules : function(cm){
31494         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
31495         Roo.util.CSS.removeStyleSheet(rulesId);
31496         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
31497             var cid = cm.getColumnId(i);
31498             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
31499                          this.tdSelector, cid, " {\n}\n",
31500                          this.hdSelector, cid, " {\n}\n",
31501                          this.splitSelector, cid, " {\n}\n");
31502         }
31503         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
31504     }
31505 });/*
31506  * Based on:
31507  * Ext JS Library 1.1.1
31508  * Copyright(c) 2006-2007, Ext JS, LLC.
31509  *
31510  * Originally Released Under LGPL - original licence link has changed is not relivant.
31511  *
31512  * Fork - LGPL
31513  * <script type="text/javascript">
31514  */
31515
31516 // private
31517 // This is a support class used internally by the Grid components
31518 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
31519     this.grid = grid;
31520     this.view = grid.getView();
31521     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
31522     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
31523     if(hd2){
31524         this.setHandleElId(Roo.id(hd));
31525         this.setOuterHandleElId(Roo.id(hd2));
31526     }
31527     this.scroll = false;
31528 };
31529 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
31530     maxDragWidth: 120,
31531     getDragData : function(e){
31532         var t = Roo.lib.Event.getTarget(e);
31533         var h = this.view.findHeaderCell(t);
31534         if(h){
31535             return {ddel: h.firstChild, header:h};
31536         }
31537         return false;
31538     },
31539
31540     onInitDrag : function(e){
31541         this.view.headersDisabled = true;
31542         var clone = this.dragData.ddel.cloneNode(true);
31543         clone.id = Roo.id();
31544         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
31545         this.proxy.update(clone);
31546         return true;
31547     },
31548
31549     afterValidDrop : function(){
31550         var v = this.view;
31551         setTimeout(function(){
31552             v.headersDisabled = false;
31553         }, 50);
31554     },
31555
31556     afterInvalidDrop : function(){
31557         var v = this.view;
31558         setTimeout(function(){
31559             v.headersDisabled = false;
31560         }, 50);
31561     }
31562 });
31563 /*
31564  * Based on:
31565  * Ext JS Library 1.1.1
31566  * Copyright(c) 2006-2007, Ext JS, LLC.
31567  *
31568  * Originally Released Under LGPL - original licence link has changed is not relivant.
31569  *
31570  * Fork - LGPL
31571  * <script type="text/javascript">
31572  */
31573 // private
31574 // This is a support class used internally by the Grid components
31575 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
31576     this.grid = grid;
31577     this.view = grid.getView();
31578     // split the proxies so they don't interfere with mouse events
31579     this.proxyTop = Roo.DomHelper.append(document.body, {
31580         cls:"col-move-top", html:"&#160;"
31581     }, true);
31582     this.proxyBottom = Roo.DomHelper.append(document.body, {
31583         cls:"col-move-bottom", html:"&#160;"
31584     }, true);
31585     this.proxyTop.hide = this.proxyBottom.hide = function(){
31586         this.setLeftTop(-100,-100);
31587         this.setStyle("visibility", "hidden");
31588     };
31589     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
31590     // temporarily disabled
31591     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
31592     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
31593 };
31594 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
31595     proxyOffsets : [-4, -9],
31596     fly: Roo.Element.fly,
31597
31598     getTargetFromEvent : function(e){
31599         var t = Roo.lib.Event.getTarget(e);
31600         var cindex = this.view.findCellIndex(t);
31601         if(cindex !== false){
31602             return this.view.getHeaderCell(cindex);
31603         }
31604     },
31605
31606     nextVisible : function(h){
31607         var v = this.view, cm = this.grid.colModel;
31608         h = h.nextSibling;
31609         while(h){
31610             if(!cm.isHidden(v.getCellIndex(h))){
31611                 return h;
31612             }
31613             h = h.nextSibling;
31614         }
31615         return null;
31616     },
31617
31618     prevVisible : function(h){
31619         var v = this.view, cm = this.grid.colModel;
31620         h = h.prevSibling;
31621         while(h){
31622             if(!cm.isHidden(v.getCellIndex(h))){
31623                 return h;
31624             }
31625             h = h.prevSibling;
31626         }
31627         return null;
31628     },
31629
31630     positionIndicator : function(h, n, e){
31631         var x = Roo.lib.Event.getPageX(e);
31632         var r = Roo.lib.Dom.getRegion(n.firstChild);
31633         var px, pt, py = r.top + this.proxyOffsets[1];
31634         if((r.right - x) <= (r.right-r.left)/2){
31635             px = r.right+this.view.borderWidth;
31636             pt = "after";
31637         }else{
31638             px = r.left;
31639             pt = "before";
31640         }
31641         var oldIndex = this.view.getCellIndex(h);
31642         var newIndex = this.view.getCellIndex(n);
31643
31644         if(this.grid.colModel.isFixed(newIndex)){
31645             return false;
31646         }
31647
31648         var locked = this.grid.colModel.isLocked(newIndex);
31649
31650         if(pt == "after"){
31651             newIndex++;
31652         }
31653         if(oldIndex < newIndex){
31654             newIndex--;
31655         }
31656         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
31657             return false;
31658         }
31659         px +=  this.proxyOffsets[0];
31660         this.proxyTop.setLeftTop(px, py);
31661         this.proxyTop.show();
31662         if(!this.bottomOffset){
31663             this.bottomOffset = this.view.mainHd.getHeight();
31664         }
31665         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
31666         this.proxyBottom.show();
31667         return pt;
31668     },
31669
31670     onNodeEnter : function(n, dd, e, data){
31671         if(data.header != n){
31672             this.positionIndicator(data.header, n, e);
31673         }
31674     },
31675
31676     onNodeOver : function(n, dd, e, data){
31677         var result = false;
31678         if(data.header != n){
31679             result = this.positionIndicator(data.header, n, e);
31680         }
31681         if(!result){
31682             this.proxyTop.hide();
31683             this.proxyBottom.hide();
31684         }
31685         return result ? this.dropAllowed : this.dropNotAllowed;
31686     },
31687
31688     onNodeOut : function(n, dd, e, data){
31689         this.proxyTop.hide();
31690         this.proxyBottom.hide();
31691     },
31692
31693     onNodeDrop : function(n, dd, e, data){
31694         var h = data.header;
31695         if(h != n){
31696             var cm = this.grid.colModel;
31697             var x = Roo.lib.Event.getPageX(e);
31698             var r = Roo.lib.Dom.getRegion(n.firstChild);
31699             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
31700             var oldIndex = this.view.getCellIndex(h);
31701             var newIndex = this.view.getCellIndex(n);
31702             var locked = cm.isLocked(newIndex);
31703             if(pt == "after"){
31704                 newIndex++;
31705             }
31706             if(oldIndex < newIndex){
31707                 newIndex--;
31708             }
31709             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
31710                 return false;
31711             }
31712             cm.setLocked(oldIndex, locked, true);
31713             cm.moveColumn(oldIndex, newIndex);
31714             this.grid.fireEvent("columnmove", oldIndex, newIndex);
31715             return true;
31716         }
31717         return false;
31718     }
31719 });
31720 /*
31721  * Based on:
31722  * Ext JS Library 1.1.1
31723  * Copyright(c) 2006-2007, Ext JS, LLC.
31724  *
31725  * Originally Released Under LGPL - original licence link has changed is not relivant.
31726  *
31727  * Fork - LGPL
31728  * <script type="text/javascript">
31729  */
31730   
31731 /**
31732  * @class Roo.grid.GridView
31733  * @extends Roo.util.Observable
31734  *
31735  * @constructor
31736  * @param {Object} config
31737  */
31738 Roo.grid.GridView = function(config){
31739     Roo.grid.GridView.superclass.constructor.call(this);
31740     this.el = null;
31741
31742     Roo.apply(this, config);
31743 };
31744
31745 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
31746
31747     /**
31748      * Override this function to apply custom css classes to rows during rendering
31749      * @param {Record} record The record
31750      * @param {Number} index
31751      * @method getRowClass
31752      */
31753     rowClass : "x-grid-row",
31754
31755     cellClass : "x-grid-col",
31756
31757     tdClass : "x-grid-td",
31758
31759     hdClass : "x-grid-hd",
31760
31761     splitClass : "x-grid-split",
31762
31763     sortClasses : ["sort-asc", "sort-desc"],
31764
31765     enableMoveAnim : false,
31766
31767     hlColor: "C3DAF9",
31768
31769     dh : Roo.DomHelper,
31770
31771     fly : Roo.Element.fly,
31772
31773     css : Roo.util.CSS,
31774
31775     borderWidth: 1,
31776
31777     splitOffset: 3,
31778
31779     scrollIncrement : 22,
31780
31781     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
31782
31783     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
31784
31785     bind : function(ds, cm){
31786         if(this.ds){
31787             this.ds.un("load", this.onLoad, this);
31788             this.ds.un("datachanged", this.onDataChange, this);
31789             this.ds.un("add", this.onAdd, this);
31790             this.ds.un("remove", this.onRemove, this);
31791             this.ds.un("update", this.onUpdate, this);
31792             this.ds.un("clear", this.onClear, this);
31793         }
31794         if(ds){
31795             ds.on("load", this.onLoad, this);
31796             ds.on("datachanged", this.onDataChange, this);
31797             ds.on("add", this.onAdd, this);
31798             ds.on("remove", this.onRemove, this);
31799             ds.on("update", this.onUpdate, this);
31800             ds.on("clear", this.onClear, this);
31801         }
31802         this.ds = ds;
31803
31804         if(this.cm){
31805             this.cm.un("widthchange", this.onColWidthChange, this);
31806             this.cm.un("headerchange", this.onHeaderChange, this);
31807             this.cm.un("hiddenchange", this.onHiddenChange, this);
31808             this.cm.un("columnmoved", this.onColumnMove, this);
31809             this.cm.un("columnlockchange", this.onColumnLock, this);
31810         }
31811         if(cm){
31812             this.generateRules(cm);
31813             cm.on("widthchange", this.onColWidthChange, this);
31814             cm.on("headerchange", this.onHeaderChange, this);
31815             cm.on("hiddenchange", this.onHiddenChange, this);
31816             cm.on("columnmoved", this.onColumnMove, this);
31817             cm.on("columnlockchange", this.onColumnLock, this);
31818         }
31819         this.cm = cm;
31820     },
31821
31822     init: function(grid){
31823                 Roo.grid.GridView.superclass.init.call(this, grid);
31824
31825                 this.bind(grid.dataSource, grid.colModel);
31826
31827             grid.on("headerclick", this.handleHeaderClick, this);
31828
31829         if(grid.trackMouseOver){
31830             grid.on("mouseover", this.onRowOver, this);
31831                 grid.on("mouseout", this.onRowOut, this);
31832             }
31833             grid.cancelTextSelection = function(){};
31834                 this.gridId = grid.id;
31835
31836                 var tpls = this.templates || {};
31837
31838                 if(!tpls.master){
31839                     tpls.master = new Roo.Template(
31840                        '<div class="x-grid" hidefocus="true">',
31841                           '<div class="x-grid-topbar"></div>',
31842                           '<div class="x-grid-scroller"><div></div></div>',
31843                           '<div class="x-grid-locked">',
31844                               '<div class="x-grid-header">{lockedHeader}</div>',
31845                               '<div class="x-grid-body">{lockedBody}</div>',
31846                           "</div>",
31847                           '<div class="x-grid-viewport">',
31848                               '<div class="x-grid-header">{header}</div>',
31849                               '<div class="x-grid-body">{body}</div>',
31850                           "</div>",
31851                           '<div class="x-grid-bottombar"></div>',
31852                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
31853                           '<div class="x-grid-resize-proxy">&#160;</div>',
31854                        "</div>"
31855                     );
31856                     tpls.master.disableformats = true;
31857                 }
31858
31859                 if(!tpls.header){
31860                     tpls.header = new Roo.Template(
31861                        '<table border="0" cellspacing="0" cellpadding="0">',
31862                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
31863                        "</table>{splits}"
31864                     );
31865                     tpls.header.disableformats = true;
31866                 }
31867                 tpls.header.compile();
31868
31869                 if(!tpls.hcell){
31870                     tpls.hcell = new Roo.Template(
31871                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
31872                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
31873                         "</div></td>"
31874                      );
31875                      tpls.hcell.disableFormats = true;
31876                 }
31877                 tpls.hcell.compile();
31878
31879                 if(!tpls.hsplit){
31880                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
31881                     tpls.hsplit.disableFormats = true;
31882                 }
31883                 tpls.hsplit.compile();
31884
31885                 if(!tpls.body){
31886                     tpls.body = new Roo.Template(
31887                        '<table border="0" cellspacing="0" cellpadding="0">',
31888                        "<tbody>{rows}</tbody>",
31889                        "</table>"
31890                     );
31891                     tpls.body.disableFormats = true;
31892                 }
31893                 tpls.body.compile();
31894
31895                 if(!tpls.row){
31896                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
31897                     tpls.row.disableFormats = true;
31898                 }
31899                 tpls.row.compile();
31900
31901                 if(!tpls.cell){
31902                     tpls.cell = new Roo.Template(
31903                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
31904                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
31905                         "</td>"
31906                     );
31907             tpls.cell.disableFormats = true;
31908         }
31909                 tpls.cell.compile();
31910
31911                 this.templates = tpls;
31912         },
31913
31914         // remap these for backwards compat
31915     onColWidthChange : function(){
31916         this.updateColumns.apply(this, arguments);
31917     },
31918     onHeaderChange : function(){
31919         this.updateHeaders.apply(this, arguments);
31920     }, 
31921     onHiddenChange : function(){
31922         this.handleHiddenChange.apply(this, arguments);
31923     },
31924     onColumnMove : function(){
31925         this.handleColumnMove.apply(this, arguments);
31926     },
31927     onColumnLock : function(){
31928         this.handleLockChange.apply(this, arguments);
31929     },
31930
31931     onDataChange : function(){
31932         this.refresh();
31933         this.updateHeaderSortState();
31934     },
31935
31936         onClear : function(){
31937         this.refresh();
31938     },
31939
31940         onUpdate : function(ds, record){
31941         this.refreshRow(record);
31942     },
31943
31944     refreshRow : function(record){
31945         var ds = this.ds, index;
31946         if(typeof record == 'number'){
31947             index = record;
31948             record = ds.getAt(index);
31949         }else{
31950             index = ds.indexOf(record);
31951         }
31952         this.insertRows(ds, index, index, true);
31953         this.onRemove(ds, record, index+1, true);
31954         this.syncRowHeights(index, index);
31955         this.layout();
31956         this.fireEvent("rowupdated", this, index, record);
31957     },
31958
31959     onAdd : function(ds, records, index){
31960         this.insertRows(ds, index, index + (records.length-1));
31961     },
31962
31963     onRemove : function(ds, record, index, isUpdate){
31964         if(isUpdate !== true){
31965             this.fireEvent("beforerowremoved", this, index, record);
31966         }
31967         var bt = this.getBodyTable(), lt = this.getLockedTable();
31968         if(bt.rows[index]){
31969             bt.firstChild.removeChild(bt.rows[index]);
31970         }
31971         if(lt.rows[index]){
31972             lt.firstChild.removeChild(lt.rows[index]);
31973         }
31974         if(isUpdate !== true){
31975             this.stripeRows(index);
31976             this.syncRowHeights(index, index);
31977             this.layout();
31978             this.fireEvent("rowremoved", this, index, record);
31979         }
31980     },
31981
31982     onLoad : function(){
31983         this.scrollToTop();
31984     },
31985
31986     /**
31987      * Scrolls the grid to the top
31988      */
31989     scrollToTop : function(){
31990         if(this.scroller){
31991             this.scroller.dom.scrollTop = 0;
31992             this.syncScroll();
31993         }
31994     },
31995
31996     /**
31997      * Gets a panel in the header of the grid that can be used for toolbars etc.
31998      * After modifying the contents of this panel a call to grid.autoSize() may be
31999      * required to register any changes in size.
32000      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32001      * @return Roo.Element
32002      */
32003     getHeaderPanel : function(doShow){
32004         if(doShow){
32005             this.headerPanel.show();
32006         }
32007         return this.headerPanel;
32008         },
32009
32010         /**
32011      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32012      * After modifying the contents of this panel a call to grid.autoSize() may be
32013      * required to register any changes in size.
32014      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32015      * @return Roo.Element
32016      */
32017     getFooterPanel : function(doShow){
32018         if(doShow){
32019             this.footerPanel.show();
32020         }
32021         return this.footerPanel;
32022         },
32023
32024         initElements : function(){
32025             var E = Roo.Element;
32026             var el = this.grid.getGridEl().dom.firstChild;
32027             var cs = el.childNodes;
32028
32029             this.el = new E(el);
32030             this.headerPanel = new E(el.firstChild);
32031             this.headerPanel.enableDisplayMode("block");
32032
32033         this.scroller = new E(cs[1]);
32034             this.scrollSizer = new E(this.scroller.dom.firstChild);
32035
32036             this.lockedWrap = new E(cs[2]);
32037             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
32038             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
32039
32040             this.mainWrap = new E(cs[3]);
32041             this.mainHd = new E(this.mainWrap.dom.firstChild);
32042             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
32043
32044             this.footerPanel = new E(cs[4]);
32045             this.footerPanel.enableDisplayMode("block");
32046
32047         this.focusEl = new E(cs[5]);
32048         this.focusEl.swallowEvent("click", true);
32049         this.resizeProxy = new E(cs[6]);
32050
32051             this.headerSelector = String.format(
32052                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
32053                this.lockedHd.id, this.mainHd.id
32054             );
32055
32056             this.splitterSelector = String.format(
32057                '#{0} div.x-grid-split, #{1} div.x-grid-split',
32058                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
32059             );
32060     },
32061     idToCssName : function(s)
32062     {
32063         return s.replace(/[^a-z0-9]+/ig, '-');
32064     },
32065
32066         getHeaderCell : function(index){
32067             return Roo.DomQuery.select(this.headerSelector)[index];
32068         },
32069
32070         getHeaderCellMeasure : function(index){
32071             return this.getHeaderCell(index).firstChild;
32072         },
32073
32074         getHeaderCellText : function(index){
32075             return this.getHeaderCell(index).firstChild.firstChild;
32076         },
32077
32078         getLockedTable : function(){
32079             return this.lockedBody.dom.firstChild;
32080         },
32081
32082         getBodyTable : function(){
32083             return this.mainBody.dom.firstChild;
32084         },
32085
32086         getLockedRow : function(index){
32087             return this.getLockedTable().rows[index];
32088         },
32089
32090         getRow : function(index){
32091             return this.getBodyTable().rows[index];
32092         },
32093
32094         getRowComposite : function(index){
32095             if(!this.rowEl){
32096                 this.rowEl = new Roo.CompositeElementLite();
32097             }
32098         var els = [], lrow, mrow;
32099         if(lrow = this.getLockedRow(index)){
32100             els.push(lrow);
32101         }
32102         if(mrow = this.getRow(index)){
32103             els.push(mrow);
32104         }
32105         this.rowEl.elements = els;
32106             return this.rowEl;
32107         },
32108
32109         getCell : function(rowIndex, colIndex){
32110             var locked = this.cm.getLockedCount();
32111             var source;
32112             if(colIndex < locked){
32113                 source = this.lockedBody.dom.firstChild;
32114             }else{
32115                 source = this.mainBody.dom.firstChild;
32116                 colIndex -= locked;
32117             }
32118         return source.rows[rowIndex].childNodes[colIndex];
32119         },
32120
32121         getCellText : function(rowIndex, colIndex){
32122             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
32123         },
32124
32125         getCellBox : function(cell){
32126             var b = this.fly(cell).getBox();
32127         if(Roo.isOpera){ // opera fails to report the Y
32128             b.y = cell.offsetTop + this.mainBody.getY();
32129         }
32130         return b;
32131     },
32132
32133     getCellIndex : function(cell){
32134         var id = String(cell.className).match(this.cellRE);
32135         if(id){
32136             return parseInt(id[1], 10);
32137         }
32138         return 0;
32139     },
32140
32141     findHeaderIndex : function(n){
32142         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32143         return r ? this.getCellIndex(r) : false;
32144     },
32145
32146     findHeaderCell : function(n){
32147         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32148         return r ? r : false;
32149     },
32150
32151     findRowIndex : function(n){
32152         if(!n){
32153             return false;
32154         }
32155         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
32156         return r ? r.rowIndex : false;
32157     },
32158
32159     findCellIndex : function(node){
32160         var stop = this.el.dom;
32161         while(node && node != stop){
32162             if(this.findRE.test(node.className)){
32163                 return this.getCellIndex(node);
32164             }
32165             node = node.parentNode;
32166         }
32167         return false;
32168     },
32169
32170     getColumnId : function(index){
32171             return this.cm.getColumnId(index);
32172         },
32173
32174         getSplitters : function(){
32175             if(this.splitterSelector){
32176                return Roo.DomQuery.select(this.splitterSelector);
32177             }else{
32178                 return null;
32179             }
32180         },
32181
32182         getSplitter : function(index){
32183             return this.getSplitters()[index];
32184         },
32185
32186     onRowOver : function(e, t){
32187         var row;
32188         if((row = this.findRowIndex(t)) !== false){
32189             this.getRowComposite(row).addClass("x-grid-row-over");
32190         }
32191     },
32192
32193     onRowOut : function(e, t){
32194         var row;
32195         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
32196             this.getRowComposite(row).removeClass("x-grid-row-over");
32197         }
32198     },
32199
32200     renderHeaders : function(){
32201             var cm = this.cm;
32202         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
32203         var cb = [], lb = [], sb = [], lsb = [], p = {};
32204         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32205             p.cellId = "x-grid-hd-0-" + i;
32206             p.splitId = "x-grid-csplit-0-" + i;
32207             p.id = cm.getColumnId(i);
32208             p.title = cm.getColumnTooltip(i) || "";
32209             p.value = cm.getColumnHeader(i) || "";
32210             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
32211             if(!cm.isLocked(i)){
32212                 cb[cb.length] = ct.apply(p);
32213                 sb[sb.length] = st.apply(p);
32214             }else{
32215                 lb[lb.length] = ct.apply(p);
32216                 lsb[lsb.length] = st.apply(p);
32217             }
32218         }
32219         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
32220                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
32221         },
32222
32223         updateHeaders : function(){
32224         var html = this.renderHeaders();
32225         this.lockedHd.update(html[0]);
32226         this.mainHd.update(html[1]);
32227     },
32228
32229     /**
32230      * Focuses the specified row.
32231      * @param {Number} row The row index
32232      */
32233     focusRow : function(row){
32234         var x = this.scroller.dom.scrollLeft;
32235         this.focusCell(row, 0, false);
32236         this.scroller.dom.scrollLeft = x;
32237     },
32238
32239     /**
32240      * Focuses the specified cell.
32241      * @param {Number} row The row index
32242      * @param {Number} col The column index
32243      * @param {Boolean} hscroll false to disable horizontal scrolling
32244      */
32245     focusCell : function(row, col, hscroll){
32246         var el = this.ensureVisible(row, col, hscroll);
32247         this.focusEl.alignTo(el, "tl-tl");
32248         if(Roo.isGecko){
32249             this.focusEl.focus();
32250         }else{
32251             this.focusEl.focus.defer(1, this.focusEl);
32252         }
32253     },
32254
32255     /**
32256      * Scrolls the specified cell into view
32257      * @param {Number} row The row index
32258      * @param {Number} col The column index
32259      * @param {Boolean} hscroll false to disable horizontal scrolling
32260      */
32261     ensureVisible : function(row, col, hscroll){
32262         if(typeof row != "number"){
32263             row = row.rowIndex;
32264         }
32265         if(row < 0 && row >= this.ds.getCount()){
32266             return;
32267         }
32268         col = (col !== undefined ? col : 0);
32269         var cm = this.grid.colModel;
32270         while(cm.isHidden(col)){
32271             col++;
32272         }
32273
32274         var el = this.getCell(row, col);
32275         if(!el){
32276             return;
32277         }
32278         var c = this.scroller.dom;
32279
32280         var ctop = parseInt(el.offsetTop, 10);
32281         var cleft = parseInt(el.offsetLeft, 10);
32282         var cbot = ctop + el.offsetHeight;
32283         var cright = cleft + el.offsetWidth;
32284
32285         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
32286         var stop = parseInt(c.scrollTop, 10);
32287         var sleft = parseInt(c.scrollLeft, 10);
32288         var sbot = stop + ch;
32289         var sright = sleft + c.clientWidth;
32290
32291         if(ctop < stop){
32292                 c.scrollTop = ctop;
32293         }else if(cbot > sbot){
32294             c.scrollTop = cbot-ch;
32295         }
32296
32297         if(hscroll !== false){
32298             if(cleft < sleft){
32299                 c.scrollLeft = cleft;
32300             }else if(cright > sright){
32301                 c.scrollLeft = cright-c.clientWidth;
32302             }
32303         }
32304         return el;
32305     },
32306
32307     updateColumns : function(){
32308         this.grid.stopEditing();
32309         var cm = this.grid.colModel, colIds = this.getColumnIds();
32310         //var totalWidth = cm.getTotalWidth();
32311         var pos = 0;
32312         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32313             //if(cm.isHidden(i)) continue;
32314             var w = cm.getColumnWidth(i);
32315             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32316             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32317         }
32318         this.updateSplitters();
32319     },
32320
32321     generateRules : function(cm){
32322         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
32323         Roo.util.CSS.removeStyleSheet(rulesId);
32324         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32325             var cid = cm.getColumnId(i);
32326             var align = '';
32327             if(cm.config[i].align){
32328                 align = 'text-align:'+cm.config[i].align+';';
32329             }
32330             var hidden = '';
32331             if(cm.isHidden(i)){
32332                 hidden = 'display:none;';
32333             }
32334             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
32335             ruleBuf.push(
32336                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
32337                     this.hdSelector, cid, " {\n", align, width, "}\n",
32338                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
32339                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
32340         }
32341         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32342     },
32343
32344     updateSplitters : function(){
32345         var cm = this.cm, s = this.getSplitters();
32346         if(s){ // splitters not created yet
32347             var pos = 0, locked = true;
32348             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32349                 if(cm.isHidden(i)) continue;
32350                 var w = cm.getColumnWidth(i);
32351                 if(!cm.isLocked(i) && locked){
32352                     pos = 0;
32353                     locked = false;
32354                 }
32355                 pos += w;
32356                 s[i].style.left = (pos-this.splitOffset) + "px";
32357             }
32358         }
32359     },
32360
32361     handleHiddenChange : function(colModel, colIndex, hidden){
32362         if(hidden){
32363             this.hideColumn(colIndex);
32364         }else{
32365             this.unhideColumn(colIndex);
32366         }
32367     },
32368
32369     hideColumn : function(colIndex){
32370         var cid = this.getColumnId(colIndex);
32371         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
32372         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
32373         if(Roo.isSafari){
32374             this.updateHeaders();
32375         }
32376         this.updateSplitters();
32377         this.layout();
32378     },
32379
32380     unhideColumn : function(colIndex){
32381         var cid = this.getColumnId(colIndex);
32382         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
32383         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
32384
32385         if(Roo.isSafari){
32386             this.updateHeaders();
32387         }
32388         this.updateSplitters();
32389         this.layout();
32390     },
32391
32392     insertRows : function(dm, firstRow, lastRow, isUpdate){
32393         if(firstRow == 0 && lastRow == dm.getCount()-1){
32394             this.refresh();
32395         }else{
32396             if(!isUpdate){
32397                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
32398             }
32399             var s = this.getScrollState();
32400             var markup = this.renderRows(firstRow, lastRow);
32401             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
32402             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
32403             this.restoreScroll(s);
32404             if(!isUpdate){
32405                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
32406                 this.syncRowHeights(firstRow, lastRow);
32407                 this.stripeRows(firstRow);
32408                 this.layout();
32409             }
32410         }
32411     },
32412
32413     bufferRows : function(markup, target, index){
32414         var before = null, trows = target.rows, tbody = target.tBodies[0];
32415         if(index < trows.length){
32416             before = trows[index];
32417         }
32418         var b = document.createElement("div");
32419         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
32420         var rows = b.firstChild.rows;
32421         for(var i = 0, len = rows.length; i < len; i++){
32422             if(before){
32423                 tbody.insertBefore(rows[0], before);
32424             }else{
32425                 tbody.appendChild(rows[0]);
32426             }
32427         }
32428         b.innerHTML = "";
32429         b = null;
32430     },
32431
32432     deleteRows : function(dm, firstRow, lastRow){
32433         if(dm.getRowCount()<1){
32434             this.fireEvent("beforerefresh", this);
32435             this.mainBody.update("");
32436             this.lockedBody.update("");
32437             this.fireEvent("refresh", this);
32438         }else{
32439             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
32440             var bt = this.getBodyTable();
32441             var tbody = bt.firstChild;
32442             var rows = bt.rows;
32443             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
32444                 tbody.removeChild(rows[firstRow]);
32445             }
32446             this.stripeRows(firstRow);
32447             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
32448         }
32449     },
32450
32451     updateRows : function(dataSource, firstRow, lastRow){
32452         var s = this.getScrollState();
32453         this.refresh();
32454         this.restoreScroll(s);
32455     },
32456
32457     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
32458         if(!noRefresh){
32459            this.refresh();
32460         }
32461         this.updateHeaderSortState();
32462     },
32463
32464     getScrollState : function(){
32465         var sb = this.scroller.dom;
32466         return {left: sb.scrollLeft, top: sb.scrollTop};
32467     },
32468
32469     stripeRows : function(startRow){
32470         if(!this.grid.stripeRows || this.ds.getCount() < 1){
32471             return;
32472         }
32473         startRow = startRow || 0;
32474         var rows = this.getBodyTable().rows;
32475         var lrows = this.getLockedTable().rows;
32476         var cls = ' x-grid-row-alt ';
32477         for(var i = startRow, len = rows.length; i < len; i++){
32478             var row = rows[i], lrow = lrows[i];
32479             var isAlt = ((i+1) % 2 == 0);
32480             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
32481             if(isAlt == hasAlt){
32482                 continue;
32483             }
32484             if(isAlt){
32485                 row.className += " x-grid-row-alt";
32486             }else{
32487                 row.className = row.className.replace("x-grid-row-alt", "");
32488             }
32489             if(lrow){
32490                 lrow.className = row.className;
32491             }
32492         }
32493     },
32494
32495     restoreScroll : function(state){
32496         var sb = this.scroller.dom;
32497         sb.scrollLeft = state.left;
32498         sb.scrollTop = state.top;
32499         this.syncScroll();
32500     },
32501
32502     syncScroll : function(){
32503         var sb = this.scroller.dom;
32504         var sh = this.mainHd.dom;
32505         var bs = this.mainBody.dom;
32506         var lv = this.lockedBody.dom;
32507         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
32508         lv.scrollTop = bs.scrollTop = sb.scrollTop;
32509     },
32510
32511     handleScroll : function(e){
32512         this.syncScroll();
32513         var sb = this.scroller.dom;
32514         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
32515         e.stopEvent();
32516     },
32517
32518     handleWheel : function(e){
32519         var d = e.getWheelDelta();
32520         this.scroller.dom.scrollTop -= d*22;
32521         // set this here to prevent jumpy scrolling on large tables
32522         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
32523         e.stopEvent();
32524     },
32525
32526     renderRows : function(startRow, endRow){
32527         // pull in all the crap needed to render rows
32528         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
32529         var colCount = cm.getColumnCount();
32530
32531         if(ds.getCount() < 1){
32532             return ["", ""];
32533         }
32534
32535         // build a map for all the columns
32536         var cs = [];
32537         for(var i = 0; i < colCount; i++){
32538             var name = cm.getDataIndex(i);
32539             cs[i] = {
32540                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
32541                 renderer : cm.getRenderer(i),
32542                 id : cm.getColumnId(i),
32543                 locked : cm.isLocked(i)
32544             };
32545         }
32546
32547         startRow = startRow || 0;
32548         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
32549
32550         // records to render
32551         var rs = ds.getRange(startRow, endRow);
32552
32553         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
32554     },
32555
32556     // As much as I hate to duplicate code, this was branched because FireFox really hates
32557     // [].join("") on strings. The performance difference was substantial enough to
32558     // branch this function
32559     doRender : Roo.isGecko ?
32560             function(cs, rs, ds, startRow, colCount, stripe){
32561                 var ts = this.templates, ct = ts.cell, rt = ts.row;
32562                 // buffers
32563                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
32564                 for(var j = 0, len = rs.length; j < len; j++){
32565                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
32566                     for(var i = 0; i < colCount; i++){
32567                         c = cs[i];
32568                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
32569                         p.id = c.id;
32570                         p.css = p.attr = "";
32571                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
32572                         if(p.value == undefined || p.value === "") p.value = "&#160;";
32573                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
32574                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
32575                         }
32576                         var markup = ct.apply(p);
32577                         if(!c.locked){
32578                             cb+= markup;
32579                         }else{
32580                             lcb+= markup;
32581                         }
32582                     }
32583                     var alt = [];
32584                     if(stripe && ((rowIndex+1) % 2 == 0)){
32585                         alt[0] = "x-grid-row-alt";
32586                     }
32587                     if(r.dirty){
32588                         alt[1] = " x-grid-dirty-row";
32589                     }
32590                     rp.cells = lcb;
32591                     if(this.getRowClass){
32592                         alt[2] = this.getRowClass(r, rowIndex);
32593                     }
32594                     rp.alt = alt.join(" ");
32595                     lbuf+= rt.apply(rp);
32596                     rp.cells = cb;
32597                     buf+=  rt.apply(rp);
32598                 }
32599                 return [lbuf, buf];
32600             } :
32601             function(cs, rs, ds, startRow, colCount, stripe){
32602                 var ts = this.templates, ct = ts.cell, rt = ts.row;
32603                 // buffers
32604                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
32605                 for(var j = 0, len = rs.length; j < len; j++){
32606                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
32607                     for(var i = 0; i < colCount; i++){
32608                         c = cs[i];
32609                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
32610                         p.id = c.id;
32611                         p.css = p.attr = "";
32612                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
32613                         if(p.value == undefined || p.value === "") p.value = "&#160;";
32614                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
32615                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
32616                         }
32617                         var markup = ct.apply(p);
32618                         if(!c.locked){
32619                             cb[cb.length] = markup;
32620                         }else{
32621                             lcb[lcb.length] = markup;
32622                         }
32623                     }
32624                     var alt = [];
32625                     if(stripe && ((rowIndex+1) % 2 == 0)){
32626                         alt[0] = "x-grid-row-alt";
32627                     }
32628                     if(r.dirty){
32629                         alt[1] = " x-grid-dirty-row";
32630                     }
32631                     rp.cells = lcb;
32632                     if(this.getRowClass){
32633                         alt[2] = this.getRowClass(r, rowIndex);
32634                     }
32635                     rp.alt = alt.join(" ");
32636                     rp.cells = lcb.join("");
32637                     lbuf[lbuf.length] = rt.apply(rp);
32638                     rp.cells = cb.join("");
32639                     buf[buf.length] =  rt.apply(rp);
32640                 }
32641                 return [lbuf.join(""), buf.join("")];
32642             },
32643
32644     renderBody : function(){
32645         var markup = this.renderRows();
32646         var bt = this.templates.body;
32647         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
32648     },
32649
32650     /**
32651      * Refreshes the grid
32652      * @param {Boolean} headersToo
32653      */
32654     refresh : function(headersToo){
32655         this.fireEvent("beforerefresh", this);
32656         this.grid.stopEditing();
32657         var result = this.renderBody();
32658         this.lockedBody.update(result[0]);
32659         this.mainBody.update(result[1]);
32660         if(headersToo === true){
32661             this.updateHeaders();
32662             this.updateColumns();
32663             this.updateSplitters();
32664             this.updateHeaderSortState();
32665         }
32666         this.syncRowHeights();
32667         this.layout();
32668         this.fireEvent("refresh", this);
32669     },
32670
32671     handleColumnMove : function(cm, oldIndex, newIndex){
32672         this.indexMap = null;
32673         var s = this.getScrollState();
32674         this.refresh(true);
32675         this.restoreScroll(s);
32676         this.afterMove(newIndex);
32677     },
32678
32679     afterMove : function(colIndex){
32680         if(this.enableMoveAnim && Roo.enableFx){
32681             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
32682         }
32683     },
32684
32685     updateCell : function(dm, rowIndex, dataIndex){
32686         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
32687         if(typeof colIndex == "undefined"){ // not present in grid
32688             return;
32689         }
32690         var cm = this.grid.colModel;
32691         var cell = this.getCell(rowIndex, colIndex);
32692         var cellText = this.getCellText(rowIndex, colIndex);
32693
32694         var p = {
32695             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
32696             id : cm.getColumnId(colIndex),
32697             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
32698         };
32699         var renderer = cm.getRenderer(colIndex);
32700         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
32701         if(typeof val == "undefined" || val === "") val = "&#160;";
32702         cellText.innerHTML = val;
32703         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
32704         this.syncRowHeights(rowIndex, rowIndex);
32705     },
32706
32707     calcColumnWidth : function(colIndex, maxRowsToMeasure){
32708         var maxWidth = 0;
32709         if(this.grid.autoSizeHeaders){
32710             var h = this.getHeaderCellMeasure(colIndex);
32711             maxWidth = Math.max(maxWidth, h.scrollWidth);
32712         }
32713         var tb, index;
32714         if(this.cm.isLocked(colIndex)){
32715             tb = this.getLockedTable();
32716             index = colIndex;
32717         }else{
32718             tb = this.getBodyTable();
32719             index = colIndex - this.cm.getLockedCount();
32720         }
32721         if(tb && tb.rows){
32722             var rows = tb.rows;
32723             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
32724             for(var i = 0; i < stopIndex; i++){
32725                 var cell = rows[i].childNodes[index].firstChild;
32726                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
32727             }
32728         }
32729         return maxWidth + /*margin for error in IE*/ 5;
32730     },
32731     /**
32732      * Autofit a column to its content.
32733      * @param {Number} colIndex
32734      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
32735      */
32736      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
32737          if(this.cm.isHidden(colIndex)){
32738              return; // can't calc a hidden column
32739          }
32740         if(forceMinSize){
32741             var cid = this.cm.getColumnId(colIndex);
32742             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
32743            if(this.grid.autoSizeHeaders){
32744                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
32745            }
32746         }
32747         var newWidth = this.calcColumnWidth(colIndex);
32748         this.cm.setColumnWidth(colIndex,
32749             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
32750         if(!suppressEvent){
32751             this.grid.fireEvent("columnresize", colIndex, newWidth);
32752         }
32753     },
32754
32755     /**
32756      * Autofits all columns to their content and then expands to fit any extra space in the grid
32757      */
32758      autoSizeColumns : function(){
32759         var cm = this.grid.colModel;
32760         var colCount = cm.getColumnCount();
32761         for(var i = 0; i < colCount; i++){
32762             this.autoSizeColumn(i, true, true);
32763         }
32764         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
32765             this.fitColumns();
32766         }else{
32767             this.updateColumns();
32768             this.layout();
32769         }
32770     },
32771
32772     /**
32773      * Autofits all columns to the grid's width proportionate with their current size
32774      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
32775      */
32776     fitColumns : function(reserveScrollSpace){
32777         var cm = this.grid.colModel;
32778         var colCount = cm.getColumnCount();
32779         var cols = [];
32780         var width = 0;
32781         var i, w;
32782         for (i = 0; i < colCount; i++){
32783             if(!cm.isHidden(i) && !cm.isFixed(i)){
32784                 w = cm.getColumnWidth(i);
32785                 cols.push(i);
32786                 cols.push(w);
32787                 width += w;
32788             }
32789         }
32790         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
32791         if(reserveScrollSpace){
32792             avail -= 17;
32793         }
32794         var frac = (avail - cm.getTotalWidth())/width;
32795         while (cols.length){
32796             w = cols.pop();
32797             i = cols.pop();
32798             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
32799         }
32800         this.updateColumns();
32801         this.layout();
32802     },
32803
32804     onRowSelect : function(rowIndex){
32805         var row = this.getRowComposite(rowIndex);
32806         row.addClass("x-grid-row-selected");
32807     },
32808
32809     onRowDeselect : function(rowIndex){
32810         var row = this.getRowComposite(rowIndex);
32811         row.removeClass("x-grid-row-selected");
32812     },
32813
32814     onCellSelect : function(row, col){
32815         var cell = this.getCell(row, col);
32816         if(cell){
32817             Roo.fly(cell).addClass("x-grid-cell-selected");
32818         }
32819     },
32820
32821     onCellDeselect : function(row, col){
32822         var cell = this.getCell(row, col);
32823         if(cell){
32824             Roo.fly(cell).removeClass("x-grid-cell-selected");
32825         }
32826     },
32827
32828     updateHeaderSortState : function(){
32829         var state = this.ds.getSortState();
32830         if(!state){
32831             return;
32832         }
32833         this.sortState = state;
32834         var sortColumn = this.cm.findColumnIndex(state.field);
32835         if(sortColumn != -1){
32836             var sortDir = state.direction;
32837             var sc = this.sortClasses;
32838             var hds = this.el.select(this.headerSelector).removeClass(sc);
32839             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
32840         }
32841     },
32842
32843     handleHeaderClick : function(g, index){
32844         if(this.headersDisabled){
32845             return;
32846         }
32847         var dm = g.dataSource, cm = g.colModel;
32848             if(!cm.isSortable(index)){
32849             return;
32850         }
32851             g.stopEditing();
32852         dm.sort(cm.getDataIndex(index));
32853     },
32854
32855
32856     destroy : function(){
32857         if(this.colMenu){
32858             this.colMenu.removeAll();
32859             Roo.menu.MenuMgr.unregister(this.colMenu);
32860             this.colMenu.getEl().remove();
32861             delete this.colMenu;
32862         }
32863         if(this.hmenu){
32864             this.hmenu.removeAll();
32865             Roo.menu.MenuMgr.unregister(this.hmenu);
32866             this.hmenu.getEl().remove();
32867             delete this.hmenu;
32868         }
32869         if(this.grid.enableColumnMove){
32870             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
32871             if(dds){
32872                 for(var dd in dds){
32873                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
32874                         var elid = dds[dd].dragElId;
32875                         dds[dd].unreg();
32876                         Roo.get(elid).remove();
32877                     } else if(dds[dd].config.isTarget){
32878                         dds[dd].proxyTop.remove();
32879                         dds[dd].proxyBottom.remove();
32880                         dds[dd].unreg();
32881                     }
32882                     if(Roo.dd.DDM.locationCache[dd]){
32883                         delete Roo.dd.DDM.locationCache[dd];
32884                     }
32885                 }
32886                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
32887             }
32888         }
32889         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
32890         this.bind(null, null);
32891         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
32892     },
32893
32894     handleLockChange : function(){
32895         this.refresh(true);
32896     },
32897
32898     onDenyColumnLock : function(){
32899
32900     },
32901
32902     onDenyColumnHide : function(){
32903
32904     },
32905
32906     handleHdMenuClick : function(item){
32907         var index = this.hdCtxIndex;
32908         var cm = this.cm, ds = this.ds;
32909         switch(item.id){
32910             case "asc":
32911                 ds.sort(cm.getDataIndex(index), "ASC");
32912                 break;
32913             case "desc":
32914                 ds.sort(cm.getDataIndex(index), "DESC");
32915                 break;
32916             case "lock":
32917                 var lc = cm.getLockedCount();
32918                 if(cm.getColumnCount(true) <= lc+1){
32919                     this.onDenyColumnLock();
32920                     return;
32921                 }
32922                 if(lc != index){
32923                     cm.setLocked(index, true, true);
32924                     cm.moveColumn(index, lc);
32925                     this.grid.fireEvent("columnmove", index, lc);
32926                 }else{
32927                     cm.setLocked(index, true);
32928                 }
32929             break;
32930             case "unlock":
32931                 var lc = cm.getLockedCount();
32932                 if((lc-1) != index){
32933                     cm.setLocked(index, false, true);
32934                     cm.moveColumn(index, lc-1);
32935                     this.grid.fireEvent("columnmove", index, lc-1);
32936                 }else{
32937                     cm.setLocked(index, false);
32938                 }
32939             break;
32940             default:
32941                 index = cm.getIndexById(item.id.substr(4));
32942                 if(index != -1){
32943                     if(item.checked && cm.getColumnCount(true) <= 1){
32944                         this.onDenyColumnHide();
32945                         return false;
32946                     }
32947                     cm.setHidden(index, item.checked);
32948                 }
32949         }
32950         return true;
32951     },
32952
32953     beforeColMenuShow : function(){
32954         var cm = this.cm,  colCount = cm.getColumnCount();
32955         this.colMenu.removeAll();
32956         for(var i = 0; i < colCount; i++){
32957             this.colMenu.add(new Roo.menu.CheckItem({
32958                 id: "col-"+cm.getColumnId(i),
32959                 text: cm.getColumnHeader(i),
32960                 checked: !cm.isHidden(i),
32961                 hideOnClick:false
32962             }));
32963         }
32964     },
32965
32966     handleHdCtx : function(g, index, e){
32967         e.stopEvent();
32968         var hd = this.getHeaderCell(index);
32969         this.hdCtxIndex = index;
32970         var ms = this.hmenu.items, cm = this.cm;
32971         ms.get("asc").setDisabled(!cm.isSortable(index));
32972         ms.get("desc").setDisabled(!cm.isSortable(index));
32973         if(this.grid.enableColLock !== false){
32974             ms.get("lock").setDisabled(cm.isLocked(index));
32975             ms.get("unlock").setDisabled(!cm.isLocked(index));
32976         }
32977         this.hmenu.show(hd, "tl-bl");
32978     },
32979
32980     handleHdOver : function(e){
32981         var hd = this.findHeaderCell(e.getTarget());
32982         if(hd && !this.headersDisabled){
32983             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
32984                this.fly(hd).addClass("x-grid-hd-over");
32985             }
32986         }
32987     },
32988
32989     handleHdOut : function(e){
32990         var hd = this.findHeaderCell(e.getTarget());
32991         if(hd){
32992             this.fly(hd).removeClass("x-grid-hd-over");
32993         }
32994     },
32995
32996     handleSplitDblClick : function(e, t){
32997         var i = this.getCellIndex(t);
32998         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
32999             this.autoSizeColumn(i, true);
33000             this.layout();
33001         }
33002     },
33003
33004     render : function(){
33005
33006         var cm = this.cm;
33007         var colCount = cm.getColumnCount();
33008
33009         if(this.grid.monitorWindowResize === true){
33010             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33011         }
33012         var header = this.renderHeaders();
33013         var body = this.templates.body.apply({rows:""});
33014         var html = this.templates.master.apply({
33015             lockedBody: body,
33016             body: body,
33017             lockedHeader: header[0],
33018             header: header[1]
33019         });
33020
33021         //this.updateColumns();
33022
33023         this.grid.getGridEl().dom.innerHTML = html;
33024
33025         this.initElements();
33026
33027         this.scroller.on("scroll", this.handleScroll, this);
33028         this.lockedBody.on("mousewheel", this.handleWheel, this);
33029         this.mainBody.on("mousewheel", this.handleWheel, this);
33030
33031         this.mainHd.on("mouseover", this.handleHdOver, this);
33032         this.mainHd.on("mouseout", this.handleHdOut, this);
33033         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
33034                 {delegate: "."+this.splitClass});
33035
33036         this.lockedHd.on("mouseover", this.handleHdOver, this);
33037         this.lockedHd.on("mouseout", this.handleHdOut, this);
33038         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
33039                 {delegate: "."+this.splitClass});
33040
33041         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
33042             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33043         }
33044
33045         this.updateSplitters();
33046
33047         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
33048             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33049             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33050         }
33051
33052         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
33053             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
33054             this.hmenu.add(
33055                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
33056                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
33057             );
33058             if(this.grid.enableColLock !== false){
33059                 this.hmenu.add('-',
33060                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
33061                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
33062                 );
33063             }
33064             if(this.grid.enableColumnHide !== false){
33065
33066                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
33067                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
33068                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
33069
33070                 this.hmenu.add('-',
33071                     {id:"columns", text: this.columnsText, menu: this.colMenu}
33072                 );
33073             }
33074             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
33075
33076             this.grid.on("headercontextmenu", this.handleHdCtx, this);
33077         }
33078
33079         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
33080             this.dd = new Roo.grid.GridDragZone(this.grid, {
33081                 ddGroup : this.grid.ddGroup || 'GridDD'
33082             });
33083         }
33084
33085         /*
33086         for(var i = 0; i < colCount; i++){
33087             if(cm.isHidden(i)){
33088                 this.hideColumn(i);
33089             }
33090             if(cm.config[i].align){
33091                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
33092                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
33093             }
33094         }*/
33095         
33096         this.updateHeaderSortState();
33097
33098         this.beforeInitialResize();
33099         this.layout(true);
33100
33101         // two part rendering gives faster view to the user
33102         this.renderPhase2.defer(1, this);
33103     },
33104
33105     renderPhase2 : function(){
33106         // render the rows now
33107         this.refresh();
33108         if(this.grid.autoSizeColumns){
33109             this.autoSizeColumns();
33110         }
33111     },
33112
33113     beforeInitialResize : function(){
33114
33115     },
33116
33117     onColumnSplitterMoved : function(i, w){
33118         this.userResized = true;
33119         var cm = this.grid.colModel;
33120         cm.setColumnWidth(i, w, true);
33121         var cid = cm.getColumnId(i);
33122         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33123         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33124         this.updateSplitters();
33125         this.layout();
33126         this.grid.fireEvent("columnresize", i, w);
33127     },
33128
33129     syncRowHeights : function(startIndex, endIndex){
33130         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
33131             startIndex = startIndex || 0;
33132             var mrows = this.getBodyTable().rows;
33133             var lrows = this.getLockedTable().rows;
33134             var len = mrows.length-1;
33135             endIndex = Math.min(endIndex || len, len);
33136             for(var i = startIndex; i <= endIndex; i++){
33137                 var m = mrows[i], l = lrows[i];
33138                 var h = Math.max(m.offsetHeight, l.offsetHeight);
33139                 m.style.height = l.style.height = h + "px";
33140             }
33141         }
33142     },
33143
33144     layout : function(initialRender, is2ndPass){
33145         var g = this.grid;
33146         var auto = g.autoHeight;
33147         var scrollOffset = 16;
33148         var c = g.getGridEl(), cm = this.cm,
33149                 expandCol = g.autoExpandColumn,
33150                 gv = this;
33151         //c.beginMeasure();
33152
33153         if(!c.dom.offsetWidth){ // display:none?
33154             if(initialRender){
33155                 this.lockedWrap.show();
33156                 this.mainWrap.show();
33157             }
33158             return;
33159         }
33160
33161         var hasLock = this.cm.isLocked(0);
33162
33163         var tbh = this.headerPanel.getHeight();
33164         var bbh = this.footerPanel.getHeight();
33165
33166         if(auto){
33167             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
33168             var newHeight = ch + c.getBorderWidth("tb");
33169             if(g.maxHeight){
33170                 newHeight = Math.min(g.maxHeight, newHeight);
33171             }
33172             c.setHeight(newHeight);
33173         }
33174
33175         if(g.autoWidth){
33176             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
33177         }
33178
33179         var s = this.scroller;
33180
33181         var csize = c.getSize(true);
33182
33183         this.el.setSize(csize.width, csize.height);
33184
33185         this.headerPanel.setWidth(csize.width);
33186         this.footerPanel.setWidth(csize.width);
33187
33188         var hdHeight = this.mainHd.getHeight();
33189         var vw = csize.width;
33190         var vh = csize.height - (tbh + bbh);
33191
33192         s.setSize(vw, vh);
33193
33194         var bt = this.getBodyTable();
33195         var ltWidth = hasLock ?
33196                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
33197
33198         var scrollHeight = bt.offsetHeight;
33199         var scrollWidth = ltWidth + bt.offsetWidth;
33200         var vscroll = false, hscroll = false;
33201
33202         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
33203
33204         var lw = this.lockedWrap, mw = this.mainWrap;
33205         var lb = this.lockedBody, mb = this.mainBody;
33206
33207         setTimeout(function(){
33208             var t = s.dom.offsetTop;
33209             var w = s.dom.clientWidth,
33210                 h = s.dom.clientHeight;
33211
33212             lw.setTop(t);
33213             lw.setSize(ltWidth, h);
33214
33215             mw.setLeftTop(ltWidth, t);
33216             mw.setSize(w-ltWidth, h);
33217
33218             lb.setHeight(h-hdHeight);
33219             mb.setHeight(h-hdHeight);
33220
33221             if(is2ndPass !== true && !gv.userResized && expandCol){
33222                 // high speed resize without full column calculation
33223                 
33224                 var ci = cm.getIndexById(expandCol);
33225                 if (ci < 0) {
33226                     ci = cm.findColumnIndex(expandCol);
33227                 }
33228                 ci = Math.max(0, ci); // make sure it's got at least the first col.
33229                 var expandId = cm.getColumnId(ci);
33230                 var  tw = cm.getTotalWidth(false);
33231                 var currentWidth = cm.getColumnWidth(ci);
33232                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
33233                 if(currentWidth != cw){
33234                     cm.setColumnWidth(ci, cw, true);
33235                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33236                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33237                     gv.updateSplitters();
33238                     gv.layout(false, true);
33239                 }
33240             }
33241
33242             if(initialRender){
33243                 lw.show();
33244                 mw.show();
33245             }
33246             //c.endMeasure();
33247         }, 10);
33248     },
33249
33250     onWindowResize : function(){
33251         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
33252             return;
33253         }
33254         this.layout();
33255     },
33256
33257     appendFooter : function(parentEl){
33258         return null;
33259     },
33260
33261     sortAscText : "Sort Ascending",
33262     sortDescText : "Sort Descending",
33263     lockText : "Lock Column",
33264     unlockText : "Unlock Column",
33265     columnsText : "Columns"
33266 });
33267
33268
33269 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
33270     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
33271     this.proxy.el.addClass('x-grid3-col-dd');
33272 };
33273
33274 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
33275     handleMouseDown : function(e){
33276
33277     },
33278
33279     callHandleMouseDown : function(e){
33280         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
33281     }
33282 });