Roo/Template.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({
8979     el : "my-element",
8980     template : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8981  
8982     singleSelect: true,
8983     selectedClass: "ydataview-selected",
8984     store: store
8985  });
8986
8987  // listen for node click?
8988  view.on("click", function(vw, index, node, e){
8989  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8990  });
8991
8992  // load XML data
8993  dataModel.load("foobar.xml");
8994  </code></pre>
8995  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8996  * <br><br>
8997  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8998  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8999  * 
9000  * Note: old style constructor is still suported (container, template, config)
9001  * 
9002  * @constructor
9003  * Create a new View
9004  * @param {Object} config The config object
9005  * 
9006  */
9007 Roo.View = function(config, depreciated_tpl, depreciated_config){
9008     
9009     if (typeof(depreciated_tpl) == 'undefined') {
9010         // new way.. - universal constructor.
9011         Roo.apply(this, config);
9012         this.el  = Roo.get(this.el);
9013     } else {
9014         // old format..
9015         this.el  = Roo.get(config);
9016         this.tpl = depreciated_tpl;
9017         Roo.apply(this, depreciated_config);
9018     }
9019      
9020     
9021     if(typeof(this.tpl) == "string"){
9022         this.tpl = new Roo.Template(this.tpl);
9023     } 
9024     
9025     
9026     this.tpl.compile();
9027    
9028
9029      
9030     /** @private */
9031     this.addEvents({
9032     /**
9033      * @event beforeclick
9034      * Fires before a click is processed. Returns false to cancel the default action.
9035      * @param {Roo.View} this
9036      * @param {Number} index The index of the target node
9037      * @param {HTMLElement} node The target node
9038      * @param {Roo.EventObject} e The raw event object
9039      */
9040         "beforeclick" : true,
9041     /**
9042      * @event click
9043      * Fires when a template node is clicked.
9044      * @param {Roo.View} this
9045      * @param {Number} index The index of the target node
9046      * @param {HTMLElement} node The target node
9047      * @param {Roo.EventObject} e The raw event object
9048      */
9049         "click" : true,
9050     /**
9051      * @event dblclick
9052      * Fires when a template node is double clicked.
9053      * @param {Roo.View} this
9054      * @param {Number} index The index of the target node
9055      * @param {HTMLElement} node The target node
9056      * @param {Roo.EventObject} e The raw event object
9057      */
9058         "dblclick" : true,
9059     /**
9060      * @event contextmenu
9061      * Fires when a template node is right clicked.
9062      * @param {Roo.View} this
9063      * @param {Number} index The index of the target node
9064      * @param {HTMLElement} node The target node
9065      * @param {Roo.EventObject} e The raw event object
9066      */
9067         "contextmenu" : true,
9068     /**
9069      * @event selectionchange
9070      * Fires when the selected nodes change.
9071      * @param {Roo.View} this
9072      * @param {Array} selections Array of the selected nodes
9073      */
9074         "selectionchange" : true,
9075
9076     /**
9077      * @event beforeselect
9078      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9079      * @param {Roo.View} this
9080      * @param {HTMLElement} node The node to be selected
9081      * @param {Array} selections Array of currently selected nodes
9082      */
9083         "beforeselect" : true
9084     });
9085
9086     this.el.on({
9087         "click": this.onClick,
9088         "dblclick": this.onDblClick,
9089         "contextmenu": this.onContextMenu,
9090         scope:this
9091     });
9092
9093     this.selections = [];
9094     this.nodes = [];
9095     this.cmp = new Roo.CompositeElementLite([]);
9096     if(this.store){
9097         this.store = Roo.factory(this.store, Roo.data);
9098         this.setStore(this.store, true);
9099     }
9100     Roo.View.superclass.constructor.call(this);
9101 };
9102
9103 Roo.extend(Roo.View, Roo.util.Observable, {
9104     
9105      /**
9106      * @cfg {Roo.data.Store} store Data store to load data from.
9107      */
9108     store : false,
9109     
9110     /**
9111      * @cfg {String|Roo.Element} el The container element.
9112      */
9113     el : '',
9114     
9115     /**
9116      * @cfg {String|Roo.Template} tpl The template used by this View 
9117      */
9118     tpl : false,
9119     
9120     /**
9121      * @cfg {String} selectedClass The css class to add to selected nodes
9122      */
9123     selectedClass : "x-view-selected",
9124      /**
9125      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9126      */
9127     emptyText : "",
9128     /**
9129      * Returns the element this view is bound to.
9130      * @return {Roo.Element}
9131      */
9132     getEl : function(){
9133         return this.el;
9134     },
9135
9136     /**
9137      * Refreshes the view.
9138      */
9139     refresh : function(){
9140         var t = this.tpl;
9141         this.clearSelections();
9142         this.el.update("");
9143         var html = [];
9144         var records = this.store.getRange();
9145         if(records.length < 1){
9146             this.el.update(this.emptyText);
9147             return;
9148         }
9149         for(var i = 0, len = records.length; i < len; i++){
9150             var data = this.prepareData(records[i].data, i, records[i]);
9151             html[html.length] = t.apply(data);
9152         }
9153         this.el.update(html.join(""));
9154         this.nodes = this.el.dom.childNodes;
9155         this.updateIndexes(0);
9156     },
9157
9158     /**
9159      * Function to override to reformat the data that is sent to
9160      * the template for each node.
9161      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9162      * a JSON object for an UpdateManager bound view).
9163      */
9164     prepareData : function(data){
9165         return data;
9166     },
9167
9168     onUpdate : function(ds, record){
9169         this.clearSelections();
9170         var index = this.store.indexOf(record);
9171         var n = this.nodes[index];
9172         this.tpl.insertBefore(n, this.prepareData(record.data));
9173         n.parentNode.removeChild(n);
9174         this.updateIndexes(index, index);
9175     },
9176
9177     onAdd : function(ds, records, index){
9178         this.clearSelections();
9179         if(this.nodes.length == 0){
9180             this.refresh();
9181             return;
9182         }
9183         var n = this.nodes[index];
9184         for(var i = 0, len = records.length; i < len; i++){
9185             var d = this.prepareData(records[i].data);
9186             if(n){
9187                 this.tpl.insertBefore(n, d);
9188             }else{
9189                 this.tpl.append(this.el, d);
9190             }
9191         }
9192         this.updateIndexes(index);
9193     },
9194
9195     onRemove : function(ds, record, index){
9196         this.clearSelections();
9197         this.el.dom.removeChild(this.nodes[index]);
9198         this.updateIndexes(index);
9199     },
9200
9201     /**
9202      * Refresh an individual node.
9203      * @param {Number} index
9204      */
9205     refreshNode : function(index){
9206         this.onUpdate(this.store, this.store.getAt(index));
9207     },
9208
9209     updateIndexes : function(startIndex, endIndex){
9210         var ns = this.nodes;
9211         startIndex = startIndex || 0;
9212         endIndex = endIndex || ns.length - 1;
9213         for(var i = startIndex; i <= endIndex; i++){
9214             ns[i].nodeIndex = i;
9215         }
9216     },
9217
9218     /**
9219      * Changes the data store this view uses and refresh the view.
9220      * @param {Store} store
9221      */
9222     setStore : function(store, initial){
9223         if(!initial && this.store){
9224             this.store.un("datachanged", this.refresh);
9225             this.store.un("add", this.onAdd);
9226             this.store.un("remove", this.onRemove);
9227             this.store.un("update", this.onUpdate);
9228             this.store.un("clear", this.refresh);
9229         }
9230         if(store){
9231           
9232             store.on("datachanged", this.refresh, this);
9233             store.on("add", this.onAdd, this);
9234             store.on("remove", this.onRemove, this);
9235             store.on("update", this.onUpdate, this);
9236             store.on("clear", this.refresh, this);
9237         }
9238         
9239         if(store){
9240             this.refresh();
9241         }
9242     },
9243
9244     /**
9245      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9246      * @param {HTMLElement} node
9247      * @return {HTMLElement} The template node
9248      */
9249     findItemFromChild : function(node){
9250         var el = this.el.dom;
9251         if(!node || node.parentNode == el){
9252                     return node;
9253             }
9254             var p = node.parentNode;
9255             while(p && p != el){
9256             if(p.parentNode == el){
9257                 return p;
9258             }
9259             p = p.parentNode;
9260         }
9261             return null;
9262     },
9263
9264     /** @ignore */
9265     onClick : function(e){
9266         var item = this.findItemFromChild(e.getTarget());
9267         if(item){
9268             var index = this.indexOf(item);
9269             if(this.onItemClick(item, index, e) !== false){
9270                 this.fireEvent("click", this, index, item, e);
9271             }
9272         }else{
9273             this.clearSelections();
9274         }
9275     },
9276
9277     /** @ignore */
9278     onContextMenu : function(e){
9279         var item = this.findItemFromChild(e.getTarget());
9280         if(item){
9281             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9282         }
9283     },
9284
9285     /** @ignore */
9286     onDblClick : function(e){
9287         var item = this.findItemFromChild(e.getTarget());
9288         if(item){
9289             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9290         }
9291     },
9292
9293     onItemClick : function(item, index, e){
9294         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9295             return false;
9296         }
9297         if(this.multiSelect || this.singleSelect){
9298             if(this.multiSelect && e.shiftKey && this.lastSelection){
9299                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9300             }else{
9301                 this.select(item, this.multiSelect && e.ctrlKey);
9302                 this.lastSelection = item;
9303             }
9304             e.preventDefault();
9305         }
9306         return true;
9307     },
9308
9309     /**
9310      * Get the number of selected nodes.
9311      * @return {Number}
9312      */
9313     getSelectionCount : function(){
9314         return this.selections.length;
9315     },
9316
9317     /**
9318      * Get the currently selected nodes.
9319      * @return {Array} An array of HTMLElements
9320      */
9321     getSelectedNodes : function(){
9322         return this.selections;
9323     },
9324
9325     /**
9326      * Get the indexes of the selected nodes.
9327      * @return {Array}
9328      */
9329     getSelectedIndexes : function(){
9330         var indexes = [], s = this.selections;
9331         for(var i = 0, len = s.length; i < len; i++){
9332             indexes.push(s[i].nodeIndex);
9333         }
9334         return indexes;
9335     },
9336
9337     /**
9338      * Clear all selections
9339      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9340      */
9341     clearSelections : function(suppressEvent){
9342         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9343             this.cmp.elements = this.selections;
9344             this.cmp.removeClass(this.selectedClass);
9345             this.selections = [];
9346             if(!suppressEvent){
9347                 this.fireEvent("selectionchange", this, this.selections);
9348             }
9349         }
9350     },
9351
9352     /**
9353      * Returns true if the passed node is selected
9354      * @param {HTMLElement/Number} node The node or node index
9355      * @return {Boolean}
9356      */
9357     isSelected : function(node){
9358         var s = this.selections;
9359         if(s.length < 1){
9360             return false;
9361         }
9362         node = this.getNode(node);
9363         return s.indexOf(node) !== -1;
9364     },
9365
9366     /**
9367      * Selects nodes.
9368      * @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
9369      * @param {Boolean} keepExisting (optional) true to keep existing selections
9370      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9371      */
9372     select : function(nodeInfo, keepExisting, suppressEvent){
9373         if(nodeInfo instanceof Array){
9374             if(!keepExisting){
9375                 this.clearSelections(true);
9376             }
9377             for(var i = 0, len = nodeInfo.length; i < len; i++){
9378                 this.select(nodeInfo[i], true, true);
9379             }
9380         } else{
9381             var node = this.getNode(nodeInfo);
9382             if(node && !this.isSelected(node)){
9383                 if(!keepExisting){
9384                     this.clearSelections(true);
9385                 }
9386                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9387                     Roo.fly(node).addClass(this.selectedClass);
9388                     this.selections.push(node);
9389                     if(!suppressEvent){
9390                         this.fireEvent("selectionchange", this, this.selections);
9391                     }
9392                 }
9393             }
9394         }
9395     },
9396
9397     /**
9398      * Gets a template node.
9399      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9400      * @return {HTMLElement} The node or null if it wasn't found
9401      */
9402     getNode : function(nodeInfo){
9403         if(typeof nodeInfo == "string"){
9404             return document.getElementById(nodeInfo);
9405         }else if(typeof nodeInfo == "number"){
9406             return this.nodes[nodeInfo];
9407         }
9408         return nodeInfo;
9409     },
9410
9411     /**
9412      * Gets a range template nodes.
9413      * @param {Number} startIndex
9414      * @param {Number} endIndex
9415      * @return {Array} An array of nodes
9416      */
9417     getNodes : function(start, end){
9418         var ns = this.nodes;
9419         start = start || 0;
9420         end = typeof end == "undefined" ? ns.length - 1 : end;
9421         var nodes = [];
9422         if(start <= end){
9423             for(var i = start; i <= end; i++){
9424                 nodes.push(ns[i]);
9425             }
9426         } else{
9427             for(var i = start; i >= end; i--){
9428                 nodes.push(ns[i]);
9429             }
9430         }
9431         return nodes;
9432     },
9433
9434     /**
9435      * Finds the index of the passed node
9436      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9437      * @return {Number} The index of the node or -1
9438      */
9439     indexOf : function(node){
9440         node = this.getNode(node);
9441         if(typeof node.nodeIndex == "number"){
9442             return node.nodeIndex;
9443         }
9444         var ns = this.nodes;
9445         for(var i = 0, len = ns.length; i < len; i++){
9446             if(ns[i] == node){
9447                 return i;
9448             }
9449         }
9450         return -1;
9451     }
9452 });
9453 /*
9454  * Based on:
9455  * Ext JS Library 1.1.1
9456  * Copyright(c) 2006-2007, Ext JS, LLC.
9457  *
9458  * Originally Released Under LGPL - original licence link has changed is not relivant.
9459  *
9460  * Fork - LGPL
9461  * <script type="text/javascript">
9462  */
9463
9464 /**
9465  * @class Roo.JsonView
9466  * @extends Roo.View
9467  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9468 <pre><code>
9469 var view = new Roo.JsonView({
9470     container: "my-element",
9471     template: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9472     multiSelect: true, 
9473     jsonRoot: "data" 
9474 });
9475
9476 // listen for node click?
9477 view.on("click", function(vw, index, node, e){
9478     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9479 });
9480
9481 // direct load of JSON data
9482 view.load("foobar.php");
9483
9484 // Example from my blog list
9485 var tpl = new Roo.Template(
9486     '&lt;div class="entry"&gt;' +
9487     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9488     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9489     "&lt;/div&gt;&lt;hr /&gt;"
9490 );
9491
9492 var moreView = new Roo.JsonView({
9493     container :  "entry-list", 
9494     template : tpl,
9495     jsonRoot: "posts"
9496 });
9497 moreView.on("beforerender", this.sortEntries, this);
9498 moreView.load({
9499     url: "/blog/get-posts.php",
9500     params: "allposts=true",
9501     text: "Loading Blog Entries..."
9502 });
9503 </code></pre>
9504
9505 * Note: old code is supported with arguments : (container, template, config)
9506
9507
9508  * @constructor
9509  * Create a new JsonView
9510  * 
9511  * @param {Object} config The config object
9512  * 
9513  */
9514 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9515     
9516     
9517     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9518
9519     var um = this.el.getUpdateManager();
9520     um.setRenderer(this);
9521     um.on("update", this.onLoad, this);
9522     um.on("failure", this.onLoadException, this);
9523
9524     /**
9525      * @event beforerender
9526      * Fires before rendering of the downloaded JSON data.
9527      * @param {Roo.JsonView} this
9528      * @param {Object} data The JSON data loaded
9529      */
9530     /**
9531      * @event load
9532      * Fires when data is loaded.
9533      * @param {Roo.JsonView} this
9534      * @param {Object} data The JSON data loaded
9535      * @param {Object} response The raw Connect response object
9536      */
9537     /**
9538      * @event loadexception
9539      * Fires when loading fails.
9540      * @param {Roo.JsonView} this
9541      * @param {Object} response The raw Connect response object
9542      */
9543     this.addEvents({
9544         'beforerender' : true,
9545         'load' : true,
9546         'loadexception' : true
9547     });
9548 };
9549 Roo.extend(Roo.JsonView, Roo.View, {
9550     /**
9551      * @type {String} The root property in the loaded JSON object that contains the data
9552      */
9553     jsonRoot : "",
9554
9555     /**
9556      * Refreshes the view.
9557      */
9558     refresh : function(){
9559         this.clearSelections();
9560         this.el.update("");
9561         var html = [];
9562         var o = this.jsonData;
9563         if(o && o.length > 0){
9564             for(var i = 0, len = o.length; i < len; i++){
9565                 var data = this.prepareData(o[i], i, o);
9566                 html[html.length] = this.tpl.apply(data);
9567             }
9568         }else{
9569             html.push(this.emptyText);
9570         }
9571         this.el.update(html.join(""));
9572         this.nodes = this.el.dom.childNodes;
9573         this.updateIndexes(0);
9574     },
9575
9576     /**
9577      * 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.
9578      * @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:
9579      <pre><code>
9580      view.load({
9581          url: "your-url.php",
9582          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9583          callback: yourFunction,
9584          scope: yourObject, //(optional scope)
9585          discardUrl: false,
9586          nocache: false,
9587          text: "Loading...",
9588          timeout: 30,
9589          scripts: false
9590      });
9591      </code></pre>
9592      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9593      * 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.
9594      * @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}
9595      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9596      * @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.
9597      */
9598     load : function(){
9599         var um = this.el.getUpdateManager();
9600         um.update.apply(um, arguments);
9601     },
9602
9603     render : function(el, response){
9604         this.clearSelections();
9605         this.el.update("");
9606         var o;
9607         try{
9608             o = Roo.util.JSON.decode(response.responseText);
9609             if(this.jsonRoot){
9610                 
9611                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
9612             }
9613         } catch(e){
9614         }
9615         /**
9616          * The current JSON data or null
9617          */
9618         this.jsonData = o;
9619         this.beforeRender();
9620         this.refresh();
9621     },
9622
9623 /**
9624  * Get the number of records in the current JSON dataset
9625  * @return {Number}
9626  */
9627     getCount : function(){
9628         return this.jsonData ? this.jsonData.length : 0;
9629     },
9630
9631 /**
9632  * Returns the JSON object for the specified node(s)
9633  * @param {HTMLElement/Array} node The node or an array of nodes
9634  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9635  * you get the JSON object for the node
9636  */
9637     getNodeData : function(node){
9638         if(node instanceof Array){
9639             var data = [];
9640             for(var i = 0, len = node.length; i < len; i++){
9641                 data.push(this.getNodeData(node[i]));
9642             }
9643             return data;
9644         }
9645         return this.jsonData[this.indexOf(node)] || null;
9646     },
9647
9648     beforeRender : function(){
9649         this.snapshot = this.jsonData;
9650         if(this.sortInfo){
9651             this.sort.apply(this, this.sortInfo);
9652         }
9653         this.fireEvent("beforerender", this, this.jsonData);
9654     },
9655
9656     onLoad : function(el, o){
9657         this.fireEvent("load", this, this.jsonData, o);
9658     },
9659
9660     onLoadException : function(el, o){
9661         this.fireEvent("loadexception", this, o);
9662     },
9663
9664 /**
9665  * Filter the data by a specific property.
9666  * @param {String} property A property on your JSON objects
9667  * @param {String/RegExp} value Either string that the property values
9668  * should start with, or a RegExp to test against the property
9669  */
9670     filter : function(property, value){
9671         if(this.jsonData){
9672             var data = [];
9673             var ss = this.snapshot;
9674             if(typeof value == "string"){
9675                 var vlen = value.length;
9676                 if(vlen == 0){
9677                     this.clearFilter();
9678                     return;
9679                 }
9680                 value = value.toLowerCase();
9681                 for(var i = 0, len = ss.length; i < len; i++){
9682                     var o = ss[i];
9683                     if(o[property].substr(0, vlen).toLowerCase() == value){
9684                         data.push(o);
9685                     }
9686                 }
9687             } else if(value.exec){ // regex?
9688                 for(var i = 0, len = ss.length; i < len; i++){
9689                     var o = ss[i];
9690                     if(value.test(o[property])){
9691                         data.push(o);
9692                     }
9693                 }
9694             } else{
9695                 return;
9696             }
9697             this.jsonData = data;
9698             this.refresh();
9699         }
9700     },
9701
9702 /**
9703  * Filter by a function. The passed function will be called with each
9704  * object in the current dataset. If the function returns true the value is kept,
9705  * otherwise it is filtered.
9706  * @param {Function} fn
9707  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9708  */
9709     filterBy : function(fn, scope){
9710         if(this.jsonData){
9711             var data = [];
9712             var ss = this.snapshot;
9713             for(var i = 0, len = ss.length; i < len; i++){
9714                 var o = ss[i];
9715                 if(fn.call(scope || this, o)){
9716                     data.push(o);
9717                 }
9718             }
9719             this.jsonData = data;
9720             this.refresh();
9721         }
9722     },
9723
9724 /**
9725  * Clears the current filter.
9726  */
9727     clearFilter : function(){
9728         if(this.snapshot && this.jsonData != this.snapshot){
9729             this.jsonData = this.snapshot;
9730             this.refresh();
9731         }
9732     },
9733
9734
9735 /**
9736  * Sorts the data for this view and refreshes it.
9737  * @param {String} property A property on your JSON objects to sort on
9738  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9739  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9740  */
9741     sort : function(property, dir, sortType){
9742         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9743         if(this.jsonData){
9744             var p = property;
9745             var dsc = dir && dir.toLowerCase() == "desc";
9746             var f = function(o1, o2){
9747                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9748                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9749                 ;
9750                 if(v1 < v2){
9751                     return dsc ? +1 : -1;
9752                 } else if(v1 > v2){
9753                     return dsc ? -1 : +1;
9754                 } else{
9755                     return 0;
9756                 }
9757             };
9758             this.jsonData.sort(f);
9759             this.refresh();
9760             if(this.jsonData != this.snapshot){
9761                 this.snapshot.sort(f);
9762             }
9763         }
9764     }
9765 });/*
9766  * Based on:
9767  * Ext JS Library 1.1.1
9768  * Copyright(c) 2006-2007, Ext JS, LLC.
9769  *
9770  * Originally Released Under LGPL - original licence link has changed is not relivant.
9771  *
9772  * Fork - LGPL
9773  * <script type="text/javascript">
9774  */
9775  
9776
9777 /**
9778  * @class Roo.ColorPalette
9779  * @extends Roo.Component
9780  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9781  * Here's an example of typical usage:
9782  * <pre><code>
9783 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9784 cp.render('my-div');
9785
9786 cp.on('select', function(palette, selColor){
9787     // do something with selColor
9788 });
9789 </code></pre>
9790  * @constructor
9791  * Create a new ColorPalette
9792  * @param {Object} config The config object
9793  */
9794 Roo.ColorPalette = function(config){
9795     Roo.ColorPalette.superclass.constructor.call(this, config);
9796     this.addEvents({
9797         /**
9798              * @event select
9799              * Fires when a color is selected
9800              * @param {ColorPalette} this
9801              * @param {String} color The 6-digit color hex code (without the # symbol)
9802              */
9803         select: true
9804     });
9805
9806     if(this.handler){
9807         this.on("select", this.handler, this.scope, true);
9808     }
9809 };
9810 Roo.extend(Roo.ColorPalette, Roo.Component, {
9811     /**
9812      * @cfg {String} itemCls
9813      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9814      */
9815     itemCls : "x-color-palette",
9816     /**
9817      * @cfg {String} value
9818      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9819      * the hex codes are case-sensitive.
9820      */
9821     value : null,
9822     clickEvent:'click',
9823     // private
9824     ctype: "Roo.ColorPalette",
9825
9826     /**
9827      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9828      */
9829     allowReselect : false,
9830
9831     /**
9832      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9833      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9834      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9835      * of colors with the width setting until the box is symmetrical.</p>
9836      * <p>You can override individual colors if needed:</p>
9837      * <pre><code>
9838 var cp = new Roo.ColorPalette();
9839 cp.colors[0] = "FF0000";  // change the first box to red
9840 </code></pre>
9841
9842 Or you can provide a custom array of your own for complete control:
9843 <pre><code>
9844 var cp = new Roo.ColorPalette();
9845 cp.colors = ["000000", "993300", "333300"];
9846 </code></pre>
9847      * @type Array
9848      */
9849     colors : [
9850         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9851         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9852         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9853         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9854         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9855     ],
9856
9857     // private
9858     onRender : function(container, position){
9859         var t = new Roo.MasterTemplate(
9860             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9861         );
9862         var c = this.colors;
9863         for(var i = 0, len = c.length; i < len; i++){
9864             t.add([c[i]]);
9865         }
9866         var el = document.createElement("div");
9867         el.className = this.itemCls;
9868         t.overwrite(el);
9869         container.dom.insertBefore(el, position);
9870         this.el = Roo.get(el);
9871         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9872         if(this.clickEvent != 'click'){
9873             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9874         }
9875     },
9876
9877     // private
9878     afterRender : function(){
9879         Roo.ColorPalette.superclass.afterRender.call(this);
9880         if(this.value){
9881             var s = this.value;
9882             this.value = null;
9883             this.select(s);
9884         }
9885     },
9886
9887     // private
9888     handleClick : function(e, t){
9889         e.preventDefault();
9890         if(!this.disabled){
9891             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9892             this.select(c.toUpperCase());
9893         }
9894     },
9895
9896     /**
9897      * Selects the specified color in the palette (fires the select event)
9898      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9899      */
9900     select : function(color){
9901         color = color.replace("#", "");
9902         if(color != this.value || this.allowReselect){
9903             var el = this.el;
9904             if(this.value){
9905                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9906             }
9907             el.child("a.color-"+color).addClass("x-color-palette-sel");
9908             this.value = color;
9909             this.fireEvent("select", this, color);
9910         }
9911     }
9912 });/*
9913  * Based on:
9914  * Ext JS Library 1.1.1
9915  * Copyright(c) 2006-2007, Ext JS, LLC.
9916  *
9917  * Originally Released Under LGPL - original licence link has changed is not relivant.
9918  *
9919  * Fork - LGPL
9920  * <script type="text/javascript">
9921  */
9922  
9923 /**
9924  * @class Roo.DatePicker
9925  * @extends Roo.Component
9926  * Simple date picker class.
9927  * @constructor
9928  * Create a new DatePicker
9929  * @param {Object} config The config object
9930  */
9931 Roo.DatePicker = function(config){
9932     Roo.DatePicker.superclass.constructor.call(this, config);
9933
9934     this.value = config && config.value ?
9935                  config.value.clearTime() : new Date().clearTime();
9936
9937     this.addEvents({
9938         /**
9939              * @event select
9940              * Fires when a date is selected
9941              * @param {DatePicker} this
9942              * @param {Date} date The selected date
9943              */
9944         select: true
9945     });
9946
9947     if(this.handler){
9948         this.on("select", this.handler,  this.scope || this);
9949     }
9950     // build the disabledDatesRE
9951     if(!this.disabledDatesRE && this.disabledDates){
9952         var dd = this.disabledDates;
9953         var re = "(?:";
9954         for(var i = 0; i < dd.length; i++){
9955             re += dd[i];
9956             if(i != dd.length-1) re += "|";
9957         }
9958         this.disabledDatesRE = new RegExp(re + ")");
9959     }
9960 };
9961
9962 Roo.extend(Roo.DatePicker, Roo.Component, {
9963     /**
9964      * @cfg {String} todayText
9965      * The text to display on the button that selects the current date (defaults to "Today")
9966      */
9967     todayText : "Today",
9968     /**
9969      * @cfg {String} okText
9970      * The text to display on the ok button
9971      */
9972     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9973     /**
9974      * @cfg {String} cancelText
9975      * The text to display on the cancel button
9976      */
9977     cancelText : "Cancel",
9978     /**
9979      * @cfg {String} todayTip
9980      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9981      */
9982     todayTip : "{0} (Spacebar)",
9983     /**
9984      * @cfg {Date} minDate
9985      * Minimum allowable date (JavaScript date object, defaults to null)
9986      */
9987     minDate : null,
9988     /**
9989      * @cfg {Date} maxDate
9990      * Maximum allowable date (JavaScript date object, defaults to null)
9991      */
9992     maxDate : null,
9993     /**
9994      * @cfg {String} minText
9995      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9996      */
9997     minText : "This date is before the minimum date",
9998     /**
9999      * @cfg {String} maxText
10000      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10001      */
10002     maxText : "This date is after the maximum date",
10003     /**
10004      * @cfg {String} format
10005      * The default date format string which can be overriden for localization support.  The format must be
10006      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10007      */
10008     format : "m/d/y",
10009     /**
10010      * @cfg {Array} disabledDays
10011      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10012      */
10013     disabledDays : null,
10014     /**
10015      * @cfg {String} disabledDaysText
10016      * The tooltip to display when the date falls on a disabled day (defaults to "")
10017      */
10018     disabledDaysText : "",
10019     /**
10020      * @cfg {RegExp} disabledDatesRE
10021      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10022      */
10023     disabledDatesRE : null,
10024     /**
10025      * @cfg {String} disabledDatesText
10026      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10027      */
10028     disabledDatesText : "",
10029     /**
10030      * @cfg {Boolean} constrainToViewport
10031      * True to constrain the date picker to the viewport (defaults to true)
10032      */
10033     constrainToViewport : true,
10034     /**
10035      * @cfg {Array} monthNames
10036      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10037      */
10038     monthNames : Date.monthNames,
10039     /**
10040      * @cfg {Array} dayNames
10041      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10042      */
10043     dayNames : Date.dayNames,
10044     /**
10045      * @cfg {String} nextText
10046      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10047      */
10048     nextText: 'Next Month (Control+Right)',
10049     /**
10050      * @cfg {String} prevText
10051      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10052      */
10053     prevText: 'Previous Month (Control+Left)',
10054     /**
10055      * @cfg {String} monthYearText
10056      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10057      */
10058     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10059     /**
10060      * @cfg {Number} startDay
10061      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10062      */
10063     startDay : 0,
10064     /**
10065      * @cfg {Bool} showClear
10066      * Show a clear button (usefull for date form elements that can be blank.)
10067      */
10068     
10069     showClear: false,
10070     
10071     /**
10072      * Sets the value of the date field
10073      * @param {Date} value The date to set
10074      */
10075     setValue : function(value){
10076         var old = this.value;
10077         this.value = value.clearTime(true);
10078         if(this.el){
10079             this.update(this.value);
10080         }
10081     },
10082
10083     /**
10084      * Gets the current selected value of the date field
10085      * @return {Date} The selected date
10086      */
10087     getValue : function(){
10088         return this.value;
10089     },
10090
10091     // private
10092     focus : function(){
10093         if(this.el){
10094             this.update(this.activeDate);
10095         }
10096     },
10097
10098     // private
10099     onRender : function(container, position){
10100         var m = [
10101              '<table cellspacing="0">',
10102                 '<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>',
10103                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10104         var dn = this.dayNames;
10105         for(var i = 0; i < 7; i++){
10106             var d = this.startDay+i;
10107             if(d > 6){
10108                 d = d-7;
10109             }
10110             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10111         }
10112         m[m.length] = "</tr></thead><tbody><tr>";
10113         for(var i = 0; i < 42; i++) {
10114             if(i % 7 == 0 && i != 0){
10115                 m[m.length] = "</tr><tr>";
10116             }
10117             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10118         }
10119         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10120             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10121
10122         var el = document.createElement("div");
10123         el.className = "x-date-picker";
10124         el.innerHTML = m.join("");
10125
10126         container.dom.insertBefore(el, position);
10127
10128         this.el = Roo.get(el);
10129         this.eventEl = Roo.get(el.firstChild);
10130
10131         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10132             handler: this.showPrevMonth,
10133             scope: this,
10134             preventDefault:true,
10135             stopDefault:true
10136         });
10137
10138         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10139             handler: this.showNextMonth,
10140             scope: this,
10141             preventDefault:true,
10142             stopDefault:true
10143         });
10144
10145         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10146
10147         this.monthPicker = this.el.down('div.x-date-mp');
10148         this.monthPicker.enableDisplayMode('block');
10149         
10150         var kn = new Roo.KeyNav(this.eventEl, {
10151             "left" : function(e){
10152                 e.ctrlKey ?
10153                     this.showPrevMonth() :
10154                     this.update(this.activeDate.add("d", -1));
10155             },
10156
10157             "right" : function(e){
10158                 e.ctrlKey ?
10159                     this.showNextMonth() :
10160                     this.update(this.activeDate.add("d", 1));
10161             },
10162
10163             "up" : function(e){
10164                 e.ctrlKey ?
10165                     this.showNextYear() :
10166                     this.update(this.activeDate.add("d", -7));
10167             },
10168
10169             "down" : function(e){
10170                 e.ctrlKey ?
10171                     this.showPrevYear() :
10172                     this.update(this.activeDate.add("d", 7));
10173             },
10174
10175             "pageUp" : function(e){
10176                 this.showNextMonth();
10177             },
10178
10179             "pageDown" : function(e){
10180                 this.showPrevMonth();
10181             },
10182
10183             "enter" : function(e){
10184                 e.stopPropagation();
10185                 return true;
10186             },
10187
10188             scope : this
10189         });
10190
10191         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10192
10193         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10194
10195         this.el.unselectable();
10196         
10197         this.cells = this.el.select("table.x-date-inner tbody td");
10198         this.textNodes = this.el.query("table.x-date-inner tbody span");
10199
10200         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10201             text: "&#160;",
10202             tooltip: this.monthYearText
10203         });
10204
10205         this.mbtn.on('click', this.showMonthPicker, this);
10206         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10207
10208
10209         var today = (new Date()).dateFormat(this.format);
10210         
10211         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10212         baseTb.add({
10213             text: String.format(this.todayText, today),
10214             tooltip: String.format(this.todayTip, today),
10215             handler: this.selectToday,
10216             scope: this
10217         });
10218         
10219         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10220             
10221         //});
10222         if (this.showClear) {
10223             
10224             baseTb.add( new Roo.Toolbar.Fill());
10225             baseTb.add({
10226                 text: '&#160;',
10227                 cls: 'x-btn-icon x-btn-clear',
10228                 handler: function() {
10229                     //this.value = '';
10230                     this.fireEvent("select", this, '');
10231                 },
10232                 scope: this
10233             });
10234         }
10235         
10236         
10237         if(Roo.isIE){
10238             this.el.repaint();
10239         }
10240         this.update(this.value);
10241     },
10242
10243     createMonthPicker : function(){
10244         if(!this.monthPicker.dom.firstChild){
10245             var buf = ['<table border="0" cellspacing="0">'];
10246             for(var i = 0; i < 6; i++){
10247                 buf.push(
10248                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10249                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10250                     i == 0 ?
10251                     '<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>' :
10252                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10253                 );
10254             }
10255             buf.push(
10256                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10257                     this.okText,
10258                     '</button><button type="button" class="x-date-mp-cancel">',
10259                     this.cancelText,
10260                     '</button></td></tr>',
10261                 '</table>'
10262             );
10263             this.monthPicker.update(buf.join(''));
10264             this.monthPicker.on('click', this.onMonthClick, this);
10265             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10266
10267             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10268             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10269
10270             this.mpMonths.each(function(m, a, i){
10271                 i += 1;
10272                 if((i%2) == 0){
10273                     m.dom.xmonth = 5 + Math.round(i * .5);
10274                 }else{
10275                     m.dom.xmonth = Math.round((i-1) * .5);
10276                 }
10277             });
10278         }
10279     },
10280
10281     showMonthPicker : function(){
10282         this.createMonthPicker();
10283         var size = this.el.getSize();
10284         this.monthPicker.setSize(size);
10285         this.monthPicker.child('table').setSize(size);
10286
10287         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10288         this.updateMPMonth(this.mpSelMonth);
10289         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10290         this.updateMPYear(this.mpSelYear);
10291
10292         this.monthPicker.slideIn('t', {duration:.2});
10293     },
10294
10295     updateMPYear : function(y){
10296         this.mpyear = y;
10297         var ys = this.mpYears.elements;
10298         for(var i = 1; i <= 10; i++){
10299             var td = ys[i-1], y2;
10300             if((i%2) == 0){
10301                 y2 = y + Math.round(i * .5);
10302                 td.firstChild.innerHTML = y2;
10303                 td.xyear = y2;
10304             }else{
10305                 y2 = y - (5-Math.round(i * .5));
10306                 td.firstChild.innerHTML = y2;
10307                 td.xyear = y2;
10308             }
10309             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10310         }
10311     },
10312
10313     updateMPMonth : function(sm){
10314         this.mpMonths.each(function(m, a, i){
10315             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10316         });
10317     },
10318
10319     selectMPMonth: function(m){
10320         
10321     },
10322
10323     onMonthClick : function(e, t){
10324         e.stopEvent();
10325         var el = new Roo.Element(t), pn;
10326         if(el.is('button.x-date-mp-cancel')){
10327             this.hideMonthPicker();
10328         }
10329         else if(el.is('button.x-date-mp-ok')){
10330             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10331             this.hideMonthPicker();
10332         }
10333         else if(pn = el.up('td.x-date-mp-month', 2)){
10334             this.mpMonths.removeClass('x-date-mp-sel');
10335             pn.addClass('x-date-mp-sel');
10336             this.mpSelMonth = pn.dom.xmonth;
10337         }
10338         else if(pn = el.up('td.x-date-mp-year', 2)){
10339             this.mpYears.removeClass('x-date-mp-sel');
10340             pn.addClass('x-date-mp-sel');
10341             this.mpSelYear = pn.dom.xyear;
10342         }
10343         else if(el.is('a.x-date-mp-prev')){
10344             this.updateMPYear(this.mpyear-10);
10345         }
10346         else if(el.is('a.x-date-mp-next')){
10347             this.updateMPYear(this.mpyear+10);
10348         }
10349     },
10350
10351     onMonthDblClick : function(e, t){
10352         e.stopEvent();
10353         var el = new Roo.Element(t), pn;
10354         if(pn = el.up('td.x-date-mp-month', 2)){
10355             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10356             this.hideMonthPicker();
10357         }
10358         else if(pn = el.up('td.x-date-mp-year', 2)){
10359             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10360             this.hideMonthPicker();
10361         }
10362     },
10363
10364     hideMonthPicker : function(disableAnim){
10365         if(this.monthPicker){
10366             if(disableAnim === true){
10367                 this.monthPicker.hide();
10368             }else{
10369                 this.monthPicker.slideOut('t', {duration:.2});
10370             }
10371         }
10372     },
10373
10374     // private
10375     showPrevMonth : function(e){
10376         this.update(this.activeDate.add("mo", -1));
10377     },
10378
10379     // private
10380     showNextMonth : function(e){
10381         this.update(this.activeDate.add("mo", 1));
10382     },
10383
10384     // private
10385     showPrevYear : function(){
10386         this.update(this.activeDate.add("y", -1));
10387     },
10388
10389     // private
10390     showNextYear : function(){
10391         this.update(this.activeDate.add("y", 1));
10392     },
10393
10394     // private
10395     handleMouseWheel : function(e){
10396         var delta = e.getWheelDelta();
10397         if(delta > 0){
10398             this.showPrevMonth();
10399             e.stopEvent();
10400         } else if(delta < 0){
10401             this.showNextMonth();
10402             e.stopEvent();
10403         }
10404     },
10405
10406     // private
10407     handleDateClick : function(e, t){
10408         e.stopEvent();
10409         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10410             this.setValue(new Date(t.dateValue));
10411             this.fireEvent("select", this, this.value);
10412         }
10413     },
10414
10415     // private
10416     selectToday : function(){
10417         this.setValue(new Date().clearTime());
10418         this.fireEvent("select", this, this.value);
10419     },
10420
10421     // private
10422     update : function(date){
10423         var vd = this.activeDate;
10424         this.activeDate = date;
10425         if(vd && this.el){
10426             var t = date.getTime();
10427             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10428                 this.cells.removeClass("x-date-selected");
10429                 this.cells.each(function(c){
10430                    if(c.dom.firstChild.dateValue == t){
10431                        c.addClass("x-date-selected");
10432                        setTimeout(function(){
10433                             try{c.dom.firstChild.focus();}catch(e){}
10434                        }, 50);
10435                        return false;
10436                    }
10437                 });
10438                 return;
10439             }
10440         }
10441         var days = date.getDaysInMonth();
10442         var firstOfMonth = date.getFirstDateOfMonth();
10443         var startingPos = firstOfMonth.getDay()-this.startDay;
10444
10445         if(startingPos <= this.startDay){
10446             startingPos += 7;
10447         }
10448
10449         var pm = date.add("mo", -1);
10450         var prevStart = pm.getDaysInMonth()-startingPos;
10451
10452         var cells = this.cells.elements;
10453         var textEls = this.textNodes;
10454         days += startingPos;
10455
10456         // convert everything to numbers so it's fast
10457         var day = 86400000;
10458         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10459         var today = new Date().clearTime().getTime();
10460         var sel = date.clearTime().getTime();
10461         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10462         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10463         var ddMatch = this.disabledDatesRE;
10464         var ddText = this.disabledDatesText;
10465         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10466         var ddaysText = this.disabledDaysText;
10467         var format = this.format;
10468
10469         var setCellClass = function(cal, cell){
10470             cell.title = "";
10471             var t = d.getTime();
10472             cell.firstChild.dateValue = t;
10473             if(t == today){
10474                 cell.className += " x-date-today";
10475                 cell.title = cal.todayText;
10476             }
10477             if(t == sel){
10478                 cell.className += " x-date-selected";
10479                 setTimeout(function(){
10480                     try{cell.firstChild.focus();}catch(e){}
10481                 }, 50);
10482             }
10483             // disabling
10484             if(t < min) {
10485                 cell.className = " x-date-disabled";
10486                 cell.title = cal.minText;
10487                 return;
10488             }
10489             if(t > max) {
10490                 cell.className = " x-date-disabled";
10491                 cell.title = cal.maxText;
10492                 return;
10493             }
10494             if(ddays){
10495                 if(ddays.indexOf(d.getDay()) != -1){
10496                     cell.title = ddaysText;
10497                     cell.className = " x-date-disabled";
10498                 }
10499             }
10500             if(ddMatch && format){
10501                 var fvalue = d.dateFormat(format);
10502                 if(ddMatch.test(fvalue)){
10503                     cell.title = ddText.replace("%0", fvalue);
10504                     cell.className = " x-date-disabled";
10505                 }
10506             }
10507         };
10508
10509         var i = 0;
10510         for(; i < startingPos; i++) {
10511             textEls[i].innerHTML = (++prevStart);
10512             d.setDate(d.getDate()+1);
10513             cells[i].className = "x-date-prevday";
10514             setCellClass(this, cells[i]);
10515         }
10516         for(; i < days; i++){
10517             intDay = i - startingPos + 1;
10518             textEls[i].innerHTML = (intDay);
10519             d.setDate(d.getDate()+1);
10520             cells[i].className = "x-date-active";
10521             setCellClass(this, cells[i]);
10522         }
10523         var extraDays = 0;
10524         for(; i < 42; i++) {
10525              textEls[i].innerHTML = (++extraDays);
10526              d.setDate(d.getDate()+1);
10527              cells[i].className = "x-date-nextday";
10528              setCellClass(this, cells[i]);
10529         }
10530
10531         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10532
10533         if(!this.internalRender){
10534             var main = this.el.dom.firstChild;
10535             var w = main.offsetWidth;
10536             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10537             Roo.fly(main).setWidth(w);
10538             this.internalRender = true;
10539             // opera does not respect the auto grow header center column
10540             // then, after it gets a width opera refuses to recalculate
10541             // without a second pass
10542             if(Roo.isOpera && !this.secondPass){
10543                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10544                 this.secondPass = true;
10545                 this.update.defer(10, this, [date]);
10546             }
10547         }
10548     }
10549 });/*
10550  * Based on:
10551  * Ext JS Library 1.1.1
10552  * Copyright(c) 2006-2007, Ext JS, LLC.
10553  *
10554  * Originally Released Under LGPL - original licence link has changed is not relivant.
10555  *
10556  * Fork - LGPL
10557  * <script type="text/javascript">
10558  */
10559 /**
10560  * @class Roo.TabPanel
10561  * @extends Roo.util.Observable
10562  * A lightweight tab container.
10563  * <br><br>
10564  * Usage:
10565  * <pre><code>
10566 // basic tabs 1, built from existing content
10567 var tabs = new Roo.TabPanel("tabs1");
10568 tabs.addTab("script", "View Script");
10569 tabs.addTab("markup", "View Markup");
10570 tabs.activate("script");
10571
10572 // more advanced tabs, built from javascript
10573 var jtabs = new Roo.TabPanel("jtabs");
10574 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10575
10576 // set up the UpdateManager
10577 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10578 var updater = tab2.getUpdateManager();
10579 updater.setDefaultUrl("ajax1.htm");
10580 tab2.on('activate', updater.refresh, updater, true);
10581
10582 // Use setUrl for Ajax loading
10583 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10584 tab3.setUrl("ajax2.htm", null, true);
10585
10586 // Disabled tab
10587 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10588 tab4.disable();
10589
10590 jtabs.activate("jtabs-1");
10591  * </code></pre>
10592  * @constructor
10593  * Create a new TabPanel.
10594  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10595  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10596  */
10597 Roo.TabPanel = function(container, config){
10598     /**
10599     * The container element for this TabPanel.
10600     * @type Roo.Element
10601     */
10602     this.el = Roo.get(container, true);
10603     if(config){
10604         if(typeof config == "boolean"){
10605             this.tabPosition = config ? "bottom" : "top";
10606         }else{
10607             Roo.apply(this, config);
10608         }
10609     }
10610     if(this.tabPosition == "bottom"){
10611         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10612         this.el.addClass("x-tabs-bottom");
10613     }
10614     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10615     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10616     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10617     if(Roo.isIE){
10618         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10619     }
10620     if(this.tabPosition != "bottom"){
10621     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10622      * @type Roo.Element
10623      */
10624       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10625       this.el.addClass("x-tabs-top");
10626     }
10627     this.items = [];
10628
10629     this.bodyEl.setStyle("position", "relative");
10630
10631     this.active = null;
10632     this.activateDelegate = this.activate.createDelegate(this);
10633
10634     this.addEvents({
10635         /**
10636          * @event tabchange
10637          * Fires when the active tab changes
10638          * @param {Roo.TabPanel} this
10639          * @param {Roo.TabPanelItem} activePanel The new active tab
10640          */
10641         "tabchange": true,
10642         /**
10643          * @event beforetabchange
10644          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10645          * @param {Roo.TabPanel} this
10646          * @param {Object} e Set cancel to true on this object to cancel the tab change
10647          * @param {Roo.TabPanelItem} tab The tab being changed to
10648          */
10649         "beforetabchange" : true
10650     });
10651
10652     Roo.EventManager.onWindowResize(this.onResize, this);
10653     this.cpad = this.el.getPadding("lr");
10654     this.hiddenCount = 0;
10655
10656     Roo.TabPanel.superclass.constructor.call(this);
10657 };
10658
10659 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10660         /*
10661          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10662          */
10663     tabPosition : "top",
10664         /*
10665          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10666          */
10667     currentTabWidth : 0,
10668         /*
10669          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10670          */
10671     minTabWidth : 40,
10672         /*
10673          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10674          */
10675     maxTabWidth : 250,
10676         /*
10677          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10678          */
10679     preferredTabWidth : 175,
10680         /*
10681          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10682          */
10683     resizeTabs : false,
10684         /*
10685          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10686          */
10687     monitorResize : true,
10688
10689     /**
10690      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10691      * @param {String} id The id of the div to use <b>or create</b>
10692      * @param {String} text The text for the tab
10693      * @param {String} content (optional) Content to put in the TabPanelItem body
10694      * @param {Boolean} closable (optional) True to create a close icon on the tab
10695      * @return {Roo.TabPanelItem} The created TabPanelItem
10696      */
10697     addTab : function(id, text, content, closable){
10698         var item = new Roo.TabPanelItem(this, id, text, closable);
10699         this.addTabItem(item);
10700         if(content){
10701             item.setContent(content);
10702         }
10703         return item;
10704     },
10705
10706     /**
10707      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10708      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10709      * @return {Roo.TabPanelItem}
10710      */
10711     getTab : function(id){
10712         return this.items[id];
10713     },
10714
10715     /**
10716      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10717      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10718      */
10719     hideTab : function(id){
10720         var t = this.items[id];
10721         if(!t.isHidden()){
10722            t.setHidden(true);
10723            this.hiddenCount++;
10724            this.autoSizeTabs();
10725         }
10726     },
10727
10728     /**
10729      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10730      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10731      */
10732     unhideTab : function(id){
10733         var t = this.items[id];
10734         if(t.isHidden()){
10735            t.setHidden(false);
10736            this.hiddenCount--;
10737            this.autoSizeTabs();
10738         }
10739     },
10740
10741     /**
10742      * Adds an existing {@link Roo.TabPanelItem}.
10743      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10744      */
10745     addTabItem : function(item){
10746         this.items[item.id] = item;
10747         this.items.push(item);
10748         if(this.resizeTabs){
10749            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10750            this.autoSizeTabs();
10751         }else{
10752             item.autoSize();
10753         }
10754     },
10755
10756     /**
10757      * Removes a {@link Roo.TabPanelItem}.
10758      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10759      */
10760     removeTab : function(id){
10761         var items = this.items;
10762         var tab = items[id];
10763         if(!tab) return;
10764         var index = items.indexOf(tab);
10765         if(this.active == tab && items.length > 1){
10766             var newTab = this.getNextAvailable(index);
10767             if(newTab)newTab.activate();
10768         }
10769         this.stripEl.dom.removeChild(tab.pnode.dom);
10770         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10771             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10772         }
10773         items.splice(index, 1);
10774         delete this.items[tab.id];
10775         tab.fireEvent("close", tab);
10776         tab.purgeListeners();
10777         this.autoSizeTabs();
10778     },
10779
10780     getNextAvailable : function(start){
10781         var items = this.items;
10782         var index = start;
10783         // look for a next tab that will slide over to
10784         // replace the one being removed
10785         while(index < items.length){
10786             var item = items[++index];
10787             if(item && !item.isHidden()){
10788                 return item;
10789             }
10790         }
10791         // if one isn't found select the previous tab (on the left)
10792         index = start;
10793         while(index >= 0){
10794             var item = items[--index];
10795             if(item && !item.isHidden()){
10796                 return item;
10797             }
10798         }
10799         return null;
10800     },
10801
10802     /**
10803      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10804      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10805      */
10806     disableTab : function(id){
10807         var tab = this.items[id];
10808         if(tab && this.active != tab){
10809             tab.disable();
10810         }
10811     },
10812
10813     /**
10814      * Enables a {@link Roo.TabPanelItem} that is disabled.
10815      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10816      */
10817     enableTab : function(id){
10818         var tab = this.items[id];
10819         tab.enable();
10820     },
10821
10822     /**
10823      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10824      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10825      * @return {Roo.TabPanelItem} The TabPanelItem.
10826      */
10827     activate : function(id){
10828         var tab = this.items[id];
10829         if(!tab){
10830             return null;
10831         }
10832         if(tab == this.active || tab.disabled){
10833             return tab;
10834         }
10835         var e = {};
10836         this.fireEvent("beforetabchange", this, e, tab);
10837         if(e.cancel !== true && !tab.disabled){
10838             if(this.active){
10839                 this.active.hide();
10840             }
10841             this.active = this.items[id];
10842             this.active.show();
10843             this.fireEvent("tabchange", this, this.active);
10844         }
10845         return tab;
10846     },
10847
10848     /**
10849      * Gets the active {@link Roo.TabPanelItem}.
10850      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10851      */
10852     getActiveTab : function(){
10853         return this.active;
10854     },
10855
10856     /**
10857      * Updates the tab body element to fit the height of the container element
10858      * for overflow scrolling
10859      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10860      */
10861     syncHeight : function(targetHeight){
10862         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10863         var bm = this.bodyEl.getMargins();
10864         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10865         this.bodyEl.setHeight(newHeight);
10866         return newHeight;
10867     },
10868
10869     onResize : function(){
10870         if(this.monitorResize){
10871             this.autoSizeTabs();
10872         }
10873     },
10874
10875     /**
10876      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10877      */
10878     beginUpdate : function(){
10879         this.updating = true;
10880     },
10881
10882     /**
10883      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10884      */
10885     endUpdate : function(){
10886         this.updating = false;
10887         this.autoSizeTabs();
10888     },
10889
10890     /**
10891      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10892      */
10893     autoSizeTabs : function(){
10894         var count = this.items.length;
10895         var vcount = count - this.hiddenCount;
10896         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10897         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10898         var availWidth = Math.floor(w / vcount);
10899         var b = this.stripBody;
10900         if(b.getWidth() > w){
10901             var tabs = this.items;
10902             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10903             if(availWidth < this.minTabWidth){
10904                 /*if(!this.sleft){    // incomplete scrolling code
10905                     this.createScrollButtons();
10906                 }
10907                 this.showScroll();
10908                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10909             }
10910         }else{
10911             if(this.currentTabWidth < this.preferredTabWidth){
10912                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10913             }
10914         }
10915     },
10916
10917     /**
10918      * Returns the number of tabs in this TabPanel.
10919      * @return {Number}
10920      */
10921      getCount : function(){
10922          return this.items.length;
10923      },
10924
10925     /**
10926      * Resizes all the tabs to the passed width
10927      * @param {Number} The new width
10928      */
10929     setTabWidth : function(width){
10930         this.currentTabWidth = width;
10931         for(var i = 0, len = this.items.length; i < len; i++) {
10932                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10933         }
10934     },
10935
10936     /**
10937      * Destroys this TabPanel
10938      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10939      */
10940     destroy : function(removeEl){
10941         Roo.EventManager.removeResizeListener(this.onResize, this);
10942         for(var i = 0, len = this.items.length; i < len; i++){
10943             this.items[i].purgeListeners();
10944         }
10945         if(removeEl === true){
10946             this.el.update("");
10947             this.el.remove();
10948         }
10949     }
10950 });
10951
10952 /**
10953  * @class Roo.TabPanelItem
10954  * @extends Roo.util.Observable
10955  * Represents an individual item (tab plus body) in a TabPanel.
10956  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10957  * @param {String} id The id of this TabPanelItem
10958  * @param {String} text The text for the tab of this TabPanelItem
10959  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10960  */
10961 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10962     /**
10963      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10964      * @type Roo.TabPanel
10965      */
10966     this.tabPanel = tabPanel;
10967     /**
10968      * The id for this TabPanelItem
10969      * @type String
10970      */
10971     this.id = id;
10972     /** @private */
10973     this.disabled = false;
10974     /** @private */
10975     this.text = text;
10976     /** @private */
10977     this.loaded = false;
10978     this.closable = closable;
10979
10980     /**
10981      * The body element for this TabPanelItem.
10982      * @type Roo.Element
10983      */
10984     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10985     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10986     this.bodyEl.setStyle("display", "block");
10987     this.bodyEl.setStyle("zoom", "1");
10988     this.hideAction();
10989
10990     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10991     /** @private */
10992     this.el = Roo.get(els.el, true);
10993     this.inner = Roo.get(els.inner, true);
10994     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10995     this.pnode = Roo.get(els.el.parentNode, true);
10996     this.el.on("mousedown", this.onTabMouseDown, this);
10997     this.el.on("click", this.onTabClick, this);
10998     /** @private */
10999     if(closable){
11000         var c = Roo.get(els.close, true);
11001         c.dom.title = this.closeText;
11002         c.addClassOnOver("close-over");
11003         c.on("click", this.closeClick, this);
11004      }
11005
11006     this.addEvents({
11007          /**
11008          * @event activate
11009          * Fires when this tab becomes the active tab.
11010          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11011          * @param {Roo.TabPanelItem} this
11012          */
11013         "activate": true,
11014         /**
11015          * @event beforeclose
11016          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11017          * @param {Roo.TabPanelItem} this
11018          * @param {Object} e Set cancel to true on this object to cancel the close.
11019          */
11020         "beforeclose": true,
11021         /**
11022          * @event close
11023          * Fires when this tab is closed.
11024          * @param {Roo.TabPanelItem} this
11025          */
11026          "close": true,
11027         /**
11028          * @event deactivate
11029          * Fires when this tab is no longer the active tab.
11030          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11031          * @param {Roo.TabPanelItem} this
11032          */
11033          "deactivate" : true
11034     });
11035     this.hidden = false;
11036
11037     Roo.TabPanelItem.superclass.constructor.call(this);
11038 };
11039
11040 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11041     purgeListeners : function(){
11042        Roo.util.Observable.prototype.purgeListeners.call(this);
11043        this.el.removeAllListeners();
11044     },
11045     /**
11046      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11047      */
11048     show : function(){
11049         this.pnode.addClass("on");
11050         this.showAction();
11051         if(Roo.isOpera){
11052             this.tabPanel.stripWrap.repaint();
11053         }
11054         this.fireEvent("activate", this.tabPanel, this);
11055     },
11056
11057     /**
11058      * Returns true if this tab is the active tab.
11059      * @return {Boolean}
11060      */
11061     isActive : function(){
11062         return this.tabPanel.getActiveTab() == this;
11063     },
11064
11065     /**
11066      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11067      */
11068     hide : function(){
11069         this.pnode.removeClass("on");
11070         this.hideAction();
11071         this.fireEvent("deactivate", this.tabPanel, this);
11072     },
11073
11074     hideAction : function(){
11075         this.bodyEl.hide();
11076         this.bodyEl.setStyle("position", "absolute");
11077         this.bodyEl.setLeft("-20000px");
11078         this.bodyEl.setTop("-20000px");
11079     },
11080
11081     showAction : function(){
11082         this.bodyEl.setStyle("position", "relative");
11083         this.bodyEl.setTop("");
11084         this.bodyEl.setLeft("");
11085         this.bodyEl.show();
11086     },
11087
11088     /**
11089      * Set the tooltip for the tab.
11090      * @param {String} tooltip The tab's tooltip
11091      */
11092     setTooltip : function(text){
11093         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11094             this.textEl.dom.qtip = text;
11095             this.textEl.dom.removeAttribute('title');
11096         }else{
11097             this.textEl.dom.title = text;
11098         }
11099     },
11100
11101     onTabClick : function(e){
11102         e.preventDefault();
11103         this.tabPanel.activate(this.id);
11104     },
11105
11106     onTabMouseDown : function(e){
11107         e.preventDefault();
11108         this.tabPanel.activate(this.id);
11109     },
11110
11111     getWidth : function(){
11112         return this.inner.getWidth();
11113     },
11114
11115     setWidth : function(width){
11116         var iwidth = width - this.pnode.getPadding("lr");
11117         this.inner.setWidth(iwidth);
11118         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11119         this.pnode.setWidth(width);
11120     },
11121
11122     /**
11123      * Show or hide the tab
11124      * @param {Boolean} hidden True to hide or false to show.
11125      */
11126     setHidden : function(hidden){
11127         this.hidden = hidden;
11128         this.pnode.setStyle("display", hidden ? "none" : "");
11129     },
11130
11131     /**
11132      * Returns true if this tab is "hidden"
11133      * @return {Boolean}
11134      */
11135     isHidden : function(){
11136         return this.hidden;
11137     },
11138
11139     /**
11140      * Returns the text for this tab
11141      * @return {String}
11142      */
11143     getText : function(){
11144         return this.text;
11145     },
11146
11147     autoSize : function(){
11148         //this.el.beginMeasure();
11149         this.textEl.setWidth(1);
11150         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11151         //this.el.endMeasure();
11152     },
11153
11154     /**
11155      * Sets the text for the tab (Note: this also sets the tooltip text)
11156      * @param {String} text The tab's text and tooltip
11157      */
11158     setText : function(text){
11159         this.text = text;
11160         this.textEl.update(text);
11161         this.setTooltip(text);
11162         if(!this.tabPanel.resizeTabs){
11163             this.autoSize();
11164         }
11165     },
11166     /**
11167      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11168      */
11169     activate : function(){
11170         this.tabPanel.activate(this.id);
11171     },
11172
11173     /**
11174      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11175      */
11176     disable : function(){
11177         if(this.tabPanel.active != this){
11178             this.disabled = true;
11179             this.pnode.addClass("disabled");
11180         }
11181     },
11182
11183     /**
11184      * Enables this TabPanelItem if it was previously disabled.
11185      */
11186     enable : function(){
11187         this.disabled = false;
11188         this.pnode.removeClass("disabled");
11189     },
11190
11191     /**
11192      * Sets the content for this TabPanelItem.
11193      * @param {String} content The content
11194      * @param {Boolean} loadScripts true to look for and load scripts
11195      */
11196     setContent : function(content, loadScripts){
11197         this.bodyEl.update(content, loadScripts);
11198     },
11199
11200     /**
11201      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11202      * @return {Roo.UpdateManager} The UpdateManager
11203      */
11204     getUpdateManager : function(){
11205         return this.bodyEl.getUpdateManager();
11206     },
11207
11208     /**
11209      * Set a URL to be used to load the content for this TabPanelItem.
11210      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11211      * @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)
11212      * @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)
11213      * @return {Roo.UpdateManager} The UpdateManager
11214      */
11215     setUrl : function(url, params, loadOnce){
11216         if(this.refreshDelegate){
11217             this.un('activate', this.refreshDelegate);
11218         }
11219         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11220         this.on("activate", this.refreshDelegate);
11221         return this.bodyEl.getUpdateManager();
11222     },
11223
11224     /** @private */
11225     _handleRefresh : function(url, params, loadOnce){
11226         if(!loadOnce || !this.loaded){
11227             var updater = this.bodyEl.getUpdateManager();
11228             updater.update(url, params, this._setLoaded.createDelegate(this));
11229         }
11230     },
11231
11232     /**
11233      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11234      *   Will fail silently if the setUrl method has not been called.
11235      *   This does not activate the panel, just updates its content.
11236      */
11237     refresh : function(){
11238         if(this.refreshDelegate){
11239            this.loaded = false;
11240            this.refreshDelegate();
11241         }
11242     },
11243
11244     /** @private */
11245     _setLoaded : function(){
11246         this.loaded = true;
11247     },
11248
11249     /** @private */
11250     closeClick : function(e){
11251         var o = {};
11252         e.stopEvent();
11253         this.fireEvent("beforeclose", this, o);
11254         if(o.cancel !== true){
11255             this.tabPanel.removeTab(this.id);
11256         }
11257     },
11258     /**
11259      * The text displayed in the tooltip for the close icon.
11260      * @type String
11261      */
11262     closeText : "Close this tab"
11263 });
11264
11265 /** @private */
11266 Roo.TabPanel.prototype.createStrip = function(container){
11267     var strip = document.createElement("div");
11268     strip.className = "x-tabs-wrap";
11269     container.appendChild(strip);
11270     return strip;
11271 };
11272 /** @private */
11273 Roo.TabPanel.prototype.createStripList = function(strip){
11274     // div wrapper for retard IE
11275     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>';
11276     return strip.firstChild.firstChild.firstChild.firstChild;
11277 };
11278 /** @private */
11279 Roo.TabPanel.prototype.createBody = function(container){
11280     var body = document.createElement("div");
11281     Roo.id(body, "tab-body");
11282     Roo.fly(body).addClass("x-tabs-body");
11283     container.appendChild(body);
11284     return body;
11285 };
11286 /** @private */
11287 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11288     var body = Roo.getDom(id);
11289     if(!body){
11290         body = document.createElement("div");
11291         body.id = id;
11292     }
11293     Roo.fly(body).addClass("x-tabs-item-body");
11294     bodyEl.insertBefore(body, bodyEl.firstChild);
11295     return body;
11296 };
11297 /** @private */
11298 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11299     var td = document.createElement("td");
11300     stripEl.appendChild(td);
11301     if(closable){
11302         td.className = "x-tabs-closable";
11303         if(!this.closeTpl){
11304             this.closeTpl = new Roo.Template(
11305                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11306                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11307                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11308             );
11309         }
11310         var el = this.closeTpl.overwrite(td, {"text": text});
11311         var close = el.getElementsByTagName("div")[0];
11312         var inner = el.getElementsByTagName("em")[0];
11313         return {"el": el, "close": close, "inner": inner};
11314     } else {
11315         if(!this.tabTpl){
11316             this.tabTpl = new Roo.Template(
11317                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11318                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11319             );
11320         }
11321         var el = this.tabTpl.overwrite(td, {"text": text});
11322         var inner = el.getElementsByTagName("em")[0];
11323         return {"el": el, "inner": inner};
11324     }
11325 };/*
11326  * Based on:
11327  * Ext JS Library 1.1.1
11328  * Copyright(c) 2006-2007, Ext JS, LLC.
11329  *
11330  * Originally Released Under LGPL - original licence link has changed is not relivant.
11331  *
11332  * Fork - LGPL
11333  * <script type="text/javascript">
11334  */
11335
11336 /**
11337  * @class Roo.Button
11338  * @extends Roo.util.Observable
11339  * Simple Button class
11340  * @cfg {String} text The button text
11341  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11342  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11343  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11344  * @cfg {Object} scope The scope of the handler
11345  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11346  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11347  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11348  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11349  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11350  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11351    applies if enableToggle = true)
11352  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11353  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11354   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11355  * @constructor
11356  * Create a new button
11357  * @param {Object} config The config object
11358  */
11359 Roo.Button = function(renderTo, config)
11360 {
11361     if (!config) {
11362         config = renderTo;
11363         renderTo = config.renderTo || false;
11364     }
11365     
11366     Roo.apply(this, config);
11367     this.addEvents({
11368         /**
11369              * @event click
11370              * Fires when this button is clicked
11371              * @param {Button} this
11372              * @param {EventObject} e The click event
11373              */
11374             "click" : true,
11375         /**
11376              * @event toggle
11377              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11378              * @param {Button} this
11379              * @param {Boolean} pressed
11380              */
11381             "toggle" : true,
11382         /**
11383              * @event mouseover
11384              * Fires when the mouse hovers over the button
11385              * @param {Button} this
11386              * @param {Event} e The event object
11387              */
11388         'mouseover' : true,
11389         /**
11390              * @event mouseout
11391              * Fires when the mouse exits the button
11392              * @param {Button} this
11393              * @param {Event} e The event object
11394              */
11395         'mouseout': true,
11396          /**
11397              * @event render
11398              * Fires when the button is rendered
11399              * @param {Button} this
11400              */
11401         'render': true
11402     });
11403     if(this.menu){
11404         this.menu = Roo.menu.MenuMgr.get(this.menu);
11405     }
11406     if(renderTo){
11407         this.render(renderTo);
11408     }
11409     
11410     Roo.util.Observable.call(this);
11411 };
11412
11413 Roo.extend(Roo.Button, Roo.util.Observable, {
11414     /**
11415      * 
11416      */
11417     
11418     /**
11419      * Read-only. True if this button is hidden
11420      * @type Boolean
11421      */
11422     hidden : false,
11423     /**
11424      * Read-only. True if this button is disabled
11425      * @type Boolean
11426      */
11427     disabled : false,
11428     /**
11429      * Read-only. True if this button is pressed (only if enableToggle = true)
11430      * @type Boolean
11431      */
11432     pressed : false,
11433
11434     /**
11435      * @cfg {Number} tabIndex 
11436      * The DOM tabIndex for this button (defaults to undefined)
11437      */
11438     tabIndex : undefined,
11439
11440     /**
11441      * @cfg {Boolean} enableToggle
11442      * True to enable pressed/not pressed toggling (defaults to false)
11443      */
11444     enableToggle: false,
11445     /**
11446      * @cfg {Mixed} menu
11447      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11448      */
11449     menu : undefined,
11450     /**
11451      * @cfg {String} menuAlign
11452      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11453      */
11454     menuAlign : "tl-bl?",
11455
11456     /**
11457      * @cfg {String} iconCls
11458      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11459      */
11460     iconCls : undefined,
11461     /**
11462      * @cfg {String} type
11463      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11464      */
11465     type : 'button',
11466
11467     // private
11468     menuClassTarget: 'tr',
11469
11470     /**
11471      * @cfg {String} clickEvent
11472      * The type of event to map to the button's event handler (defaults to 'click')
11473      */
11474     clickEvent : 'click',
11475
11476     /**
11477      * @cfg {Boolean} handleMouseEvents
11478      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11479      */
11480     handleMouseEvents : true,
11481
11482     /**
11483      * @cfg {String} tooltipType
11484      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11485      */
11486     tooltipType : 'qtip',
11487
11488     /**
11489      * @cfg {String} cls
11490      * A CSS class to apply to the button's main element.
11491      */
11492     
11493     /**
11494      * @cfg {Roo.Template} template (Optional)
11495      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11496      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11497      * require code modifications if required elements (e.g. a button) aren't present.
11498      */
11499
11500     // private
11501     render : function(renderTo){
11502         var btn;
11503         if(this.hideParent){
11504             this.parentEl = Roo.get(renderTo);
11505         }
11506         if(!this.dhconfig){
11507             if(!this.template){
11508                 if(!Roo.Button.buttonTemplate){
11509                     // hideous table template
11510                     Roo.Button.buttonTemplate = new Roo.Template(
11511                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11512                         '<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>',
11513                         "</tr></tbody></table>");
11514                 }
11515                 this.template = Roo.Button.buttonTemplate;
11516             }
11517             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11518             var btnEl = btn.child("button:first");
11519             btnEl.on('focus', this.onFocus, this);
11520             btnEl.on('blur', this.onBlur, this);
11521             if(this.cls){
11522                 btn.addClass(this.cls);
11523             }
11524             if(this.icon){
11525                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11526             }
11527             if(this.iconCls){
11528                 btnEl.addClass(this.iconCls);
11529                 if(!this.cls){
11530                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11531                 }
11532             }
11533             if(this.tabIndex !== undefined){
11534                 btnEl.dom.tabIndex = this.tabIndex;
11535             }
11536             if(this.tooltip){
11537                 if(typeof this.tooltip == 'object'){
11538                     Roo.QuickTips.tips(Roo.apply({
11539                           target: btnEl.id
11540                     }, this.tooltip));
11541                 } else {
11542                     btnEl.dom[this.tooltipType] = this.tooltip;
11543                 }
11544             }
11545         }else{
11546             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11547         }
11548         this.el = btn;
11549         if(this.id){
11550             this.el.dom.id = this.el.id = this.id;
11551         }
11552         if(this.menu){
11553             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11554             this.menu.on("show", this.onMenuShow, this);
11555             this.menu.on("hide", this.onMenuHide, this);
11556         }
11557         btn.addClass("x-btn");
11558         if(Roo.isIE && !Roo.isIE7){
11559             this.autoWidth.defer(1, this);
11560         }else{
11561             this.autoWidth();
11562         }
11563         if(this.handleMouseEvents){
11564             btn.on("mouseover", this.onMouseOver, this);
11565             btn.on("mouseout", this.onMouseOut, this);
11566             btn.on("mousedown", this.onMouseDown, this);
11567         }
11568         btn.on(this.clickEvent, this.onClick, this);
11569         //btn.on("mouseup", this.onMouseUp, this);
11570         if(this.hidden){
11571             this.hide();
11572         }
11573         if(this.disabled){
11574             this.disable();
11575         }
11576         Roo.ButtonToggleMgr.register(this);
11577         if(this.pressed){
11578             this.el.addClass("x-btn-pressed");
11579         }
11580         if(this.repeat){
11581             var repeater = new Roo.util.ClickRepeater(btn,
11582                 typeof this.repeat == "object" ? this.repeat : {}
11583             );
11584             repeater.on("click", this.onClick,  this);
11585         }
11586         this.fireEvent('render', this);
11587         
11588     },
11589     /**
11590      * Returns the button's underlying element
11591      * @return {Roo.Element} The element
11592      */
11593     getEl : function(){
11594         return this.el;  
11595     },
11596     
11597     /**
11598      * Destroys this Button and removes any listeners.
11599      */
11600     destroy : function(){
11601         Roo.ButtonToggleMgr.unregister(this);
11602         this.el.removeAllListeners();
11603         this.purgeListeners();
11604         this.el.remove();
11605     },
11606
11607     // private
11608     autoWidth : function(){
11609         if(this.el){
11610             this.el.setWidth("auto");
11611             if(Roo.isIE7 && Roo.isStrict){
11612                 var ib = this.el.child('button');
11613                 if(ib && ib.getWidth() > 20){
11614                     ib.clip();
11615                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11616                 }
11617             }
11618             if(this.minWidth){
11619                 if(this.hidden){
11620                     this.el.beginMeasure();
11621                 }
11622                 if(this.el.getWidth() < this.minWidth){
11623                     this.el.setWidth(this.minWidth);
11624                 }
11625                 if(this.hidden){
11626                     this.el.endMeasure();
11627                 }
11628             }
11629         }
11630     },
11631
11632     /**
11633      * Assigns this button's click handler
11634      * @param {Function} handler The function to call when the button is clicked
11635      * @param {Object} scope (optional) Scope for the function passed in
11636      */
11637     setHandler : function(handler, scope){
11638         this.handler = handler;
11639         this.scope = scope;  
11640     },
11641     
11642     /**
11643      * Sets this button's text
11644      * @param {String} text The button text
11645      */
11646     setText : function(text){
11647         this.text = text;
11648         if(this.el){
11649             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11650         }
11651         this.autoWidth();
11652     },
11653     
11654     /**
11655      * Gets the text for this button
11656      * @return {String} The button text
11657      */
11658     getText : function(){
11659         return this.text;  
11660     },
11661     
11662     /**
11663      * Show this button
11664      */
11665     show: function(){
11666         this.hidden = false;
11667         if(this.el){
11668             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11669         }
11670     },
11671     
11672     /**
11673      * Hide this button
11674      */
11675     hide: function(){
11676         this.hidden = true;
11677         if(this.el){
11678             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11679         }
11680     },
11681     
11682     /**
11683      * Convenience function for boolean show/hide
11684      * @param {Boolean} visible True to show, false to hide
11685      */
11686     setVisible: function(visible){
11687         if(visible) {
11688             this.show();
11689         }else{
11690             this.hide();
11691         }
11692     },
11693     
11694     /**
11695      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11696      * @param {Boolean} state (optional) Force a particular state
11697      */
11698     toggle : function(state){
11699         state = state === undefined ? !this.pressed : state;
11700         if(state != this.pressed){
11701             if(state){
11702                 this.el.addClass("x-btn-pressed");
11703                 this.pressed = true;
11704                 this.fireEvent("toggle", this, true);
11705             }else{
11706                 this.el.removeClass("x-btn-pressed");
11707                 this.pressed = false;
11708                 this.fireEvent("toggle", this, false);
11709             }
11710             if(this.toggleHandler){
11711                 this.toggleHandler.call(this.scope || this, this, state);
11712             }
11713         }
11714     },
11715     
11716     /**
11717      * Focus the button
11718      */
11719     focus : function(){
11720         this.el.child('button:first').focus();
11721     },
11722     
11723     /**
11724      * Disable this button
11725      */
11726     disable : function(){
11727         if(this.el){
11728             this.el.addClass("x-btn-disabled");
11729         }
11730         this.disabled = true;
11731     },
11732     
11733     /**
11734      * Enable this button
11735      */
11736     enable : function(){
11737         if(this.el){
11738             this.el.removeClass("x-btn-disabled");
11739         }
11740         this.disabled = false;
11741     },
11742
11743     /**
11744      * Convenience function for boolean enable/disable
11745      * @param {Boolean} enabled True to enable, false to disable
11746      */
11747     setDisabled : function(v){
11748         this[v !== true ? "enable" : "disable"]();
11749     },
11750
11751     // private
11752     onClick : function(e){
11753         if(e){
11754             e.preventDefault();
11755         }
11756         if(e.button != 0){
11757             return;
11758         }
11759         if(!this.disabled){
11760             if(this.enableToggle){
11761                 this.toggle();
11762             }
11763             if(this.menu && !this.menu.isVisible()){
11764                 this.menu.show(this.el, this.menuAlign);
11765             }
11766             this.fireEvent("click", this, e);
11767             if(this.handler){
11768                 this.el.removeClass("x-btn-over");
11769                 this.handler.call(this.scope || this, this, e);
11770             }
11771         }
11772     },
11773     // private
11774     onMouseOver : function(e){
11775         if(!this.disabled){
11776             this.el.addClass("x-btn-over");
11777             this.fireEvent('mouseover', this, e);
11778         }
11779     },
11780     // private
11781     onMouseOut : function(e){
11782         if(!e.within(this.el,  true)){
11783             this.el.removeClass("x-btn-over");
11784             this.fireEvent('mouseout', this, e);
11785         }
11786     },
11787     // private
11788     onFocus : function(e){
11789         if(!this.disabled){
11790             this.el.addClass("x-btn-focus");
11791         }
11792     },
11793     // private
11794     onBlur : function(e){
11795         this.el.removeClass("x-btn-focus");
11796     },
11797     // private
11798     onMouseDown : function(e){
11799         if(!this.disabled && e.button == 0){
11800             this.el.addClass("x-btn-click");
11801             Roo.get(document).on('mouseup', this.onMouseUp, this);
11802         }
11803     },
11804     // private
11805     onMouseUp : function(e){
11806         if(e.button == 0){
11807             this.el.removeClass("x-btn-click");
11808             Roo.get(document).un('mouseup', this.onMouseUp, this);
11809         }
11810     },
11811     // private
11812     onMenuShow : function(e){
11813         this.el.addClass("x-btn-menu-active");
11814     },
11815     // private
11816     onMenuHide : function(e){
11817         this.el.removeClass("x-btn-menu-active");
11818     }   
11819 });
11820
11821 // Private utility class used by Button
11822 Roo.ButtonToggleMgr = function(){
11823    var groups = {};
11824    
11825    function toggleGroup(btn, state){
11826        if(state){
11827            var g = groups[btn.toggleGroup];
11828            for(var i = 0, l = g.length; i < l; i++){
11829                if(g[i] != btn){
11830                    g[i].toggle(false);
11831                }
11832            }
11833        }
11834    }
11835    
11836    return {
11837        register : function(btn){
11838            if(!btn.toggleGroup){
11839                return;
11840            }
11841            var g = groups[btn.toggleGroup];
11842            if(!g){
11843                g = groups[btn.toggleGroup] = [];
11844            }
11845            g.push(btn);
11846            btn.on("toggle", toggleGroup);
11847        },
11848        
11849        unregister : function(btn){
11850            if(!btn.toggleGroup){
11851                return;
11852            }
11853            var g = groups[btn.toggleGroup];
11854            if(g){
11855                g.remove(btn);
11856                btn.un("toggle", toggleGroup);
11857            }
11858        }
11859    };
11860 }();/*
11861  * Based on:
11862  * Ext JS Library 1.1.1
11863  * Copyright(c) 2006-2007, Ext JS, LLC.
11864  *
11865  * Originally Released Under LGPL - original licence link has changed is not relivant.
11866  *
11867  * Fork - LGPL
11868  * <script type="text/javascript">
11869  */
11870  
11871 /**
11872  * @class Roo.SplitButton
11873  * @extends Roo.Button
11874  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11875  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11876  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11877  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11878  * @cfg {String} arrowTooltip The title attribute of the arrow
11879  * @constructor
11880  * Create a new menu button
11881  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11882  * @param {Object} config The config object
11883  */
11884 Roo.SplitButton = function(renderTo, config){
11885     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11886     /**
11887      * @event arrowclick
11888      * Fires when this button's arrow is clicked
11889      * @param {SplitButton} this
11890      * @param {EventObject} e The click event
11891      */
11892     this.addEvents({"arrowclick":true});
11893 };
11894
11895 Roo.extend(Roo.SplitButton, Roo.Button, {
11896     render : function(renderTo){
11897         // this is one sweet looking template!
11898         var tpl = new Roo.Template(
11899             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11900             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11901             '<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>',
11902             "</tbody></table></td><td>",
11903             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11904             '<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>',
11905             "</tbody></table></td></tr></table>"
11906         );
11907         var btn = tpl.append(renderTo, [this.text, this.type], true);
11908         var btnEl = btn.child("button");
11909         if(this.cls){
11910             btn.addClass(this.cls);
11911         }
11912         if(this.icon){
11913             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11914         }
11915         if(this.iconCls){
11916             btnEl.addClass(this.iconCls);
11917             if(!this.cls){
11918                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11919             }
11920         }
11921         this.el = btn;
11922         if(this.handleMouseEvents){
11923             btn.on("mouseover", this.onMouseOver, this);
11924             btn.on("mouseout", this.onMouseOut, this);
11925             btn.on("mousedown", this.onMouseDown, this);
11926             btn.on("mouseup", this.onMouseUp, this);
11927         }
11928         btn.on(this.clickEvent, this.onClick, this);
11929         if(this.tooltip){
11930             if(typeof this.tooltip == 'object'){
11931                 Roo.QuickTips.tips(Roo.apply({
11932                       target: btnEl.id
11933                 }, this.tooltip));
11934             } else {
11935                 btnEl.dom[this.tooltipType] = this.tooltip;
11936             }
11937         }
11938         if(this.arrowTooltip){
11939             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11940         }
11941         if(this.hidden){
11942             this.hide();
11943         }
11944         if(this.disabled){
11945             this.disable();
11946         }
11947         if(this.pressed){
11948             this.el.addClass("x-btn-pressed");
11949         }
11950         if(Roo.isIE && !Roo.isIE7){
11951             this.autoWidth.defer(1, this);
11952         }else{
11953             this.autoWidth();
11954         }
11955         if(this.menu){
11956             this.menu.on("show", this.onMenuShow, this);
11957             this.menu.on("hide", this.onMenuHide, this);
11958         }
11959         this.fireEvent('render', this);
11960     },
11961
11962     // private
11963     autoWidth : function(){
11964         if(this.el){
11965             var tbl = this.el.child("table:first");
11966             var tbl2 = this.el.child("table:last");
11967             this.el.setWidth("auto");
11968             tbl.setWidth("auto");
11969             if(Roo.isIE7 && Roo.isStrict){
11970                 var ib = this.el.child('button:first');
11971                 if(ib && ib.getWidth() > 20){
11972                     ib.clip();
11973                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11974                 }
11975             }
11976             if(this.minWidth){
11977                 if(this.hidden){
11978                     this.el.beginMeasure();
11979                 }
11980                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11981                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11982                 }
11983                 if(this.hidden){
11984                     this.el.endMeasure();
11985                 }
11986             }
11987             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11988         } 
11989     },
11990     /**
11991      * Sets this button's click handler
11992      * @param {Function} handler The function to call when the button is clicked
11993      * @param {Object} scope (optional) Scope for the function passed above
11994      */
11995     setHandler : function(handler, scope){
11996         this.handler = handler;
11997         this.scope = scope;  
11998     },
11999     
12000     /**
12001      * Sets this button's arrow click handler
12002      * @param {Function} handler The function to call when the arrow is clicked
12003      * @param {Object} scope (optional) Scope for the function passed above
12004      */
12005     setArrowHandler : function(handler, scope){
12006         this.arrowHandler = handler;
12007         this.scope = scope;  
12008     },
12009     
12010     /**
12011      * Focus the button
12012      */
12013     focus : function(){
12014         if(this.el){
12015             this.el.child("button:first").focus();
12016         }
12017     },
12018
12019     // private
12020     onClick : function(e){
12021         e.preventDefault();
12022         if(!this.disabled){
12023             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12024                 if(this.menu && !this.menu.isVisible()){
12025                     this.menu.show(this.el, this.menuAlign);
12026                 }
12027                 this.fireEvent("arrowclick", this, e);
12028                 if(this.arrowHandler){
12029                     this.arrowHandler.call(this.scope || this, this, e);
12030                 }
12031             }else{
12032                 this.fireEvent("click", this, e);
12033                 if(this.handler){
12034                     this.handler.call(this.scope || this, this, e);
12035                 }
12036             }
12037         }
12038     },
12039     // private
12040     onMouseDown : function(e){
12041         if(!this.disabled){
12042             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12043         }
12044     },
12045     // private
12046     onMouseUp : function(e){
12047         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12048     }   
12049 });
12050
12051
12052 // backwards compat
12053 Roo.MenuButton = Roo.SplitButton;/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063
12064 /**
12065  * @class Roo.Toolbar
12066  * Basic Toolbar class.
12067  * @constructor
12068  * Creates a new Toolbar
12069  * @param {Object} config The config object
12070  */ 
12071 Roo.Toolbar = function(container, buttons, config)
12072 {
12073     /// old consturctor format still supported..
12074     if(container instanceof Array){ // omit the container for later rendering
12075         buttons = container;
12076         config = buttons;
12077         container = null;
12078     }
12079     if (typeof(container) == 'object' && container.xtype) {
12080         config = container;
12081         container = config.container;
12082         buttons = config.buttons; // not really - use items!!
12083     }
12084     var xitems = [];
12085     if (config && config.items) {
12086         xitems = config.items;
12087         delete config.items;
12088     }
12089     Roo.apply(this, config);
12090     this.buttons = buttons;
12091     
12092     if(container){
12093         this.render(container);
12094     }
12095     Roo.each(xitems, function(b) {
12096         this.add(b);
12097     }, this);
12098     
12099 };
12100
12101 Roo.Toolbar.prototype = {
12102     /**
12103      * @cfg {Roo.data.Store} items
12104      * array of button configs or elements to add
12105      */
12106     
12107     /**
12108      * @cfg {String/HTMLElement/Element} container
12109      * The id or element that will contain the toolbar
12110      */
12111     // private
12112     render : function(ct){
12113         this.el = Roo.get(ct);
12114         if(this.cls){
12115             this.el.addClass(this.cls);
12116         }
12117         // using a table allows for vertical alignment
12118         // 100% width is needed by Safari...
12119         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12120         this.tr = this.el.child("tr", true);
12121         var autoId = 0;
12122         this.items = new Roo.util.MixedCollection(false, function(o){
12123             return o.id || ("item" + (++autoId));
12124         });
12125         if(this.buttons){
12126             this.add.apply(this, this.buttons);
12127             delete this.buttons;
12128         }
12129     },
12130
12131     /**
12132      * Adds element(s) to the toolbar -- this function takes a variable number of 
12133      * arguments of mixed type and adds them to the toolbar.
12134      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12135      * <ul>
12136      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12137      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12138      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12139      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12140      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12141      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12142      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12143      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12144      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12145      * </ul>
12146      * @param {Mixed} arg2
12147      * @param {Mixed} etc.
12148      */
12149     add : function(){
12150         var a = arguments, l = a.length;
12151         for(var i = 0; i < l; i++){
12152             this._add(a[i]);
12153         }
12154     },
12155     // private..
12156     _add : function(el) {
12157         
12158         if (el.xtype) {
12159             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12160         }
12161         
12162         if (el.applyTo){ // some kind of form field
12163             return this.addField(el);
12164         } 
12165         if (el.render){ // some kind of Toolbar.Item
12166             return this.addItem(el);
12167         }
12168         if (typeof el == "string"){ // string
12169             if(el == "separator" || el == "-"){
12170                 return this.addSeparator();
12171             }
12172             if (el == " "){
12173                 return this.addSpacer();
12174             }
12175             if(el == "->"){
12176                 return this.addFill();
12177             }
12178             return this.addText(el);
12179             
12180         }
12181         if(el.tagName){ // element
12182             return this.addElement(el);
12183         }
12184         if(typeof el == "object"){ // must be button config?
12185             return this.addButton(el);
12186         }
12187         // and now what?!?!
12188         return false;
12189         
12190     },
12191     
12192     /**
12193      * Add an Xtype element
12194      * @param {Object} xtype Xtype Object
12195      * @return {Object} created Object
12196      */
12197     addxtype : function(e){
12198         return this.add(e);  
12199     },
12200     
12201     /**
12202      * Returns the Element for this toolbar.
12203      * @return {Roo.Element}
12204      */
12205     getEl : function(){
12206         return this.el;  
12207     },
12208     
12209     /**
12210      * Adds a separator
12211      * @return {Roo.Toolbar.Item} The separator item
12212      */
12213     addSeparator : function(){
12214         return this.addItem(new Roo.Toolbar.Separator());
12215     },
12216
12217     /**
12218      * Adds a spacer element
12219      * @return {Roo.Toolbar.Spacer} The spacer item
12220      */
12221     addSpacer : function(){
12222         return this.addItem(new Roo.Toolbar.Spacer());
12223     },
12224
12225     /**
12226      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12227      * @return {Roo.Toolbar.Fill} The fill item
12228      */
12229     addFill : function(){
12230         return this.addItem(new Roo.Toolbar.Fill());
12231     },
12232
12233     /**
12234      * Adds any standard HTML element to the toolbar
12235      * @param {String/HTMLElement/Element} el The element or id of the element to add
12236      * @return {Roo.Toolbar.Item} The element's item
12237      */
12238     addElement : function(el){
12239         return this.addItem(new Roo.Toolbar.Item(el));
12240     },
12241     /**
12242      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12243      * @type Roo.util.MixedCollection  
12244      */
12245     items : false,
12246      
12247     /**
12248      * Adds any Toolbar.Item or subclass
12249      * @param {Roo.Toolbar.Item} item
12250      * @return {Roo.Toolbar.Item} The item
12251      */
12252     addItem : function(item){
12253         var td = this.nextBlock();
12254         item.render(td);
12255         this.items.add(item);
12256         return item;
12257     },
12258     
12259     /**
12260      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12261      * @param {Object/Array} config A button config or array of configs
12262      * @return {Roo.Toolbar.Button/Array}
12263      */
12264     addButton : function(config){
12265         if(config instanceof Array){
12266             var buttons = [];
12267             for(var i = 0, len = config.length; i < len; i++) {
12268                 buttons.push(this.addButton(config[i]));
12269             }
12270             return buttons;
12271         }
12272         var b = config;
12273         if(!(config instanceof Roo.Toolbar.Button)){
12274             b = config.split ?
12275                 new Roo.Toolbar.SplitButton(config) :
12276                 new Roo.Toolbar.Button(config);
12277         }
12278         var td = this.nextBlock();
12279         b.render(td);
12280         this.items.add(b);
12281         return b;
12282     },
12283     
12284     /**
12285      * Adds text to the toolbar
12286      * @param {String} text The text to add
12287      * @return {Roo.Toolbar.Item} The element's item
12288      */
12289     addText : function(text){
12290         return this.addItem(new Roo.Toolbar.TextItem(text));
12291     },
12292     
12293     /**
12294      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12295      * @param {Number} index The index where the item is to be inserted
12296      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12297      * @return {Roo.Toolbar.Button/Item}
12298      */
12299     insertButton : function(index, item){
12300         if(item instanceof Array){
12301             var buttons = [];
12302             for(var i = 0, len = item.length; i < len; i++) {
12303                buttons.push(this.insertButton(index + i, item[i]));
12304             }
12305             return buttons;
12306         }
12307         if (!(item instanceof Roo.Toolbar.Button)){
12308            item = new Roo.Toolbar.Button(item);
12309         }
12310         var td = document.createElement("td");
12311         this.tr.insertBefore(td, this.tr.childNodes[index]);
12312         item.render(td);
12313         this.items.insert(index, item);
12314         return item;
12315     },
12316     
12317     /**
12318      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12319      * @param {Object} config
12320      * @return {Roo.Toolbar.Item} The element's item
12321      */
12322     addDom : function(config, returnEl){
12323         var td = this.nextBlock();
12324         Roo.DomHelper.overwrite(td, config);
12325         var ti = new Roo.Toolbar.Item(td.firstChild);
12326         ti.render(td);
12327         this.items.add(ti);
12328         return ti;
12329     },
12330
12331     /**
12332      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12333      * @type Roo.util.MixedCollection  
12334      */
12335     fields : false,
12336     
12337     /**
12338      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12339      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12340      * @param {Roo.form.Field} field
12341      * @return {Roo.ToolbarItem}
12342      */
12343      
12344       
12345     addField : function(field) {
12346         if (!this.fields) {
12347             var autoId = 0;
12348             this.fields = new Roo.util.MixedCollection(false, function(o){
12349                 return o.id || ("item" + (++autoId));
12350             });
12351
12352         }
12353         
12354         var td = this.nextBlock();
12355         field.render(td);
12356         var ti = new Roo.Toolbar.Item(td.firstChild);
12357         ti.render(td);
12358         this.items.add(ti);
12359         this.fields.add(field);
12360         return ti;
12361     },
12362     /**
12363      * Hide the toolbar
12364      * @method hide
12365      */
12366      
12367       
12368     hide : function()
12369     {
12370         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12371         this.el.child('div').hide();
12372     },
12373     /**
12374      * Show the toolbar
12375      * @method show
12376      */
12377     show : function()
12378     {
12379         this.el.child('div').show();
12380     },
12381       
12382     // private
12383     nextBlock : function(){
12384         var td = document.createElement("td");
12385         this.tr.appendChild(td);
12386         return td;
12387     },
12388
12389     // private
12390     destroy : function(){
12391         if(this.items){ // rendered?
12392             Roo.destroy.apply(Roo, this.items.items);
12393         }
12394         if(this.fields){ // rendered?
12395             Roo.destroy.apply(Roo, this.fields.items);
12396         }
12397         Roo.Element.uncache(this.el, this.tr);
12398     }
12399 };
12400
12401 /**
12402  * @class Roo.Toolbar.Item
12403  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12404  * @constructor
12405  * Creates a new Item
12406  * @param {HTMLElement} el 
12407  */
12408 Roo.Toolbar.Item = function(el){
12409     this.el = Roo.getDom(el);
12410     this.id = Roo.id(this.el);
12411     this.hidden = false;
12412 };
12413
12414 Roo.Toolbar.Item.prototype = {
12415     
12416     /**
12417      * Get this item's HTML Element
12418      * @return {HTMLElement}
12419      */
12420     getEl : function(){
12421        return this.el;  
12422     },
12423
12424     // private
12425     render : function(td){
12426         this.td = td;
12427         td.appendChild(this.el);
12428     },
12429     
12430     /**
12431      * Removes and destroys this item.
12432      */
12433     destroy : function(){
12434         this.td.parentNode.removeChild(this.td);
12435     },
12436     
12437     /**
12438      * Shows this item.
12439      */
12440     show: function(){
12441         this.hidden = false;
12442         this.td.style.display = "";
12443     },
12444     
12445     /**
12446      * Hides this item.
12447      */
12448     hide: function(){
12449         this.hidden = true;
12450         this.td.style.display = "none";
12451     },
12452     
12453     /**
12454      * Convenience function for boolean show/hide.
12455      * @param {Boolean} visible true to show/false to hide
12456      */
12457     setVisible: function(visible){
12458         if(visible) {
12459             this.show();
12460         }else{
12461             this.hide();
12462         }
12463     },
12464     
12465     /**
12466      * Try to focus this item.
12467      */
12468     focus : function(){
12469         Roo.fly(this.el).focus();
12470     },
12471     
12472     /**
12473      * Disables this item.
12474      */
12475     disable : function(){
12476         Roo.fly(this.td).addClass("x-item-disabled");
12477         this.disabled = true;
12478         this.el.disabled = true;
12479     },
12480     
12481     /**
12482      * Enables this item.
12483      */
12484     enable : function(){
12485         Roo.fly(this.td).removeClass("x-item-disabled");
12486         this.disabled = false;
12487         this.el.disabled = false;
12488     }
12489 };
12490
12491
12492 /**
12493  * @class Roo.Toolbar.Separator
12494  * @extends Roo.Toolbar.Item
12495  * A simple toolbar separator class
12496  * @constructor
12497  * Creates a new Separator
12498  */
12499 Roo.Toolbar.Separator = function(){
12500     var s = document.createElement("span");
12501     s.className = "ytb-sep";
12502     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12503 };
12504 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12505     enable:Roo.emptyFn,
12506     disable:Roo.emptyFn,
12507     focus:Roo.emptyFn
12508 });
12509
12510 /**
12511  * @class Roo.Toolbar.Spacer
12512  * @extends Roo.Toolbar.Item
12513  * A simple element that adds extra horizontal space to a toolbar.
12514  * @constructor
12515  * Creates a new Spacer
12516  */
12517 Roo.Toolbar.Spacer = function(){
12518     var s = document.createElement("div");
12519     s.className = "ytb-spacer";
12520     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12521 };
12522 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12523     enable:Roo.emptyFn,
12524     disable:Roo.emptyFn,
12525     focus:Roo.emptyFn
12526 });
12527
12528 /**
12529  * @class Roo.Toolbar.Fill
12530  * @extends Roo.Toolbar.Spacer
12531  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12532  * @constructor
12533  * Creates a new Spacer
12534  */
12535 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12536     // private
12537     render : function(td){
12538         td.style.width = '100%';
12539         Roo.Toolbar.Fill.superclass.render.call(this, td);
12540     }
12541 });
12542
12543 /**
12544  * @class Roo.Toolbar.TextItem
12545  * @extends Roo.Toolbar.Item
12546  * A simple class that renders text directly into a toolbar.
12547  * @constructor
12548  * Creates a new TextItem
12549  * @param {String} text
12550  */
12551 Roo.Toolbar.TextItem = function(text){
12552     if (typeof(text) == 'object') {
12553         text = text.text;
12554     }
12555     var s = document.createElement("span");
12556     s.className = "ytb-text";
12557     s.innerHTML = text;
12558     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12559 };
12560 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12561     enable:Roo.emptyFn,
12562     disable:Roo.emptyFn,
12563     focus:Roo.emptyFn
12564 });
12565
12566 /**
12567  * @class Roo.Toolbar.Button
12568  * @extends Roo.Button
12569  * A button that renders into a toolbar.
12570  * @constructor
12571  * Creates a new Button
12572  * @param {Object} config A standard {@link Roo.Button} config object
12573  */
12574 Roo.Toolbar.Button = function(config){
12575     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12576 };
12577 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12578     render : function(td){
12579         this.td = td;
12580         Roo.Toolbar.Button.superclass.render.call(this, td);
12581     },
12582     
12583     /**
12584      * Removes and destroys this button
12585      */
12586     destroy : function(){
12587         Roo.Toolbar.Button.superclass.destroy.call(this);
12588         this.td.parentNode.removeChild(this.td);
12589     },
12590     
12591     /**
12592      * Shows this button
12593      */
12594     show: function(){
12595         this.hidden = false;
12596         this.td.style.display = "";
12597     },
12598     
12599     /**
12600      * Hides this button
12601      */
12602     hide: function(){
12603         this.hidden = true;
12604         this.td.style.display = "none";
12605     },
12606
12607     /**
12608      * Disables this item
12609      */
12610     disable : function(){
12611         Roo.fly(this.td).addClass("x-item-disabled");
12612         this.disabled = true;
12613     },
12614
12615     /**
12616      * Enables this item
12617      */
12618     enable : function(){
12619         Roo.fly(this.td).removeClass("x-item-disabled");
12620         this.disabled = false;
12621     }
12622 });
12623 // backwards compat
12624 Roo.ToolbarButton = Roo.Toolbar.Button;
12625
12626 /**
12627  * @class Roo.Toolbar.SplitButton
12628  * @extends Roo.SplitButton
12629  * A menu button that renders into a toolbar.
12630  * @constructor
12631  * Creates a new SplitButton
12632  * @param {Object} config A standard {@link Roo.SplitButton} config object
12633  */
12634 Roo.Toolbar.SplitButton = function(config){
12635     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12636 };
12637 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12638     render : function(td){
12639         this.td = td;
12640         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12641     },
12642     
12643     /**
12644      * Removes and destroys this button
12645      */
12646     destroy : function(){
12647         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12648         this.td.parentNode.removeChild(this.td);
12649     },
12650     
12651     /**
12652      * Shows this button
12653      */
12654     show: function(){
12655         this.hidden = false;
12656         this.td.style.display = "";
12657     },
12658     
12659     /**
12660      * Hides this button
12661      */
12662     hide: function(){
12663         this.hidden = true;
12664         this.td.style.display = "none";
12665     }
12666 });
12667
12668 // backwards compat
12669 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12670  * Based on:
12671  * Ext JS Library 1.1.1
12672  * Copyright(c) 2006-2007, Ext JS, LLC.
12673  *
12674  * Originally Released Under LGPL - original licence link has changed is not relivant.
12675  *
12676  * Fork - LGPL
12677  * <script type="text/javascript">
12678  */
12679  
12680 /**
12681  * @class Roo.PagingToolbar
12682  * @extends Roo.Toolbar
12683  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12684  * @constructor
12685  * Create a new PagingToolbar
12686  * @param {Object} config The config object
12687  */
12688 Roo.PagingToolbar = function(el, ds, config)
12689 {
12690     // old args format still supported... - xtype is prefered..
12691     if (typeof(el) == 'object' && el.xtype) {
12692         // created from xtype...
12693         config = el;
12694         ds = el.dataSource;
12695         el = config.container;
12696     }
12697     
12698     
12699     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12700     this.ds = ds;
12701     this.cursor = 0;
12702     this.renderButtons(this.el);
12703     this.bind(ds);
12704 };
12705
12706 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12707     /**
12708      * @cfg {Roo.data.Store} dataSource
12709      * The underlying data store providing the paged data
12710      */
12711     /**
12712      * @cfg {String/HTMLElement/Element} container
12713      * container The id or element that will contain the toolbar
12714      */
12715     /**
12716      * @cfg {Boolean} displayInfo
12717      * True to display the displayMsg (defaults to false)
12718      */
12719     /**
12720      * @cfg {Number} pageSize
12721      * The number of records to display per page (defaults to 20)
12722      */
12723     pageSize: 20,
12724     /**
12725      * @cfg {String} displayMsg
12726      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12727      */
12728     displayMsg : 'Displaying {0} - {1} of {2}',
12729     /**
12730      * @cfg {String} emptyMsg
12731      * The message to display when no records are found (defaults to "No data to display")
12732      */
12733     emptyMsg : 'No data to display',
12734     /**
12735      * Customizable piece of the default paging text (defaults to "Page")
12736      * @type String
12737      */
12738     beforePageText : "Page",
12739     /**
12740      * Customizable piece of the default paging text (defaults to "of %0")
12741      * @type String
12742      */
12743     afterPageText : "of {0}",
12744     /**
12745      * Customizable piece of the default paging text (defaults to "First Page")
12746      * @type String
12747      */
12748     firstText : "First Page",
12749     /**
12750      * Customizable piece of the default paging text (defaults to "Previous Page")
12751      * @type String
12752      */
12753     prevText : "Previous Page",
12754     /**
12755      * Customizable piece of the default paging text (defaults to "Next Page")
12756      * @type String
12757      */
12758     nextText : "Next Page",
12759     /**
12760      * Customizable piece of the default paging text (defaults to "Last Page")
12761      * @type String
12762      */
12763     lastText : "Last Page",
12764     /**
12765      * Customizable piece of the default paging text (defaults to "Refresh")
12766      * @type String
12767      */
12768     refreshText : "Refresh",
12769
12770     // private
12771     renderButtons : function(el){
12772         Roo.PagingToolbar.superclass.render.call(this, el);
12773         this.first = this.addButton({
12774             tooltip: this.firstText,
12775             cls: "x-btn-icon x-grid-page-first",
12776             disabled: true,
12777             handler: this.onClick.createDelegate(this, ["first"])
12778         });
12779         this.prev = this.addButton({
12780             tooltip: this.prevText,
12781             cls: "x-btn-icon x-grid-page-prev",
12782             disabled: true,
12783             handler: this.onClick.createDelegate(this, ["prev"])
12784         });
12785         this.addSeparator();
12786         this.add(this.beforePageText);
12787         this.field = Roo.get(this.addDom({
12788            tag: "input",
12789            type: "text",
12790            size: "3",
12791            value: "1",
12792            cls: "x-grid-page-number"
12793         }).el);
12794         this.field.on("keydown", this.onPagingKeydown, this);
12795         this.field.on("focus", function(){this.dom.select();});
12796         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12797         this.field.setHeight(18);
12798         this.addSeparator();
12799         this.next = this.addButton({
12800             tooltip: this.nextText,
12801             cls: "x-btn-icon x-grid-page-next",
12802             disabled: true,
12803             handler: this.onClick.createDelegate(this, ["next"])
12804         });
12805         this.last = this.addButton({
12806             tooltip: this.lastText,
12807             cls: "x-btn-icon x-grid-page-last",
12808             disabled: true,
12809             handler: this.onClick.createDelegate(this, ["last"])
12810         });
12811         this.addSeparator();
12812         this.loading = this.addButton({
12813             tooltip: this.refreshText,
12814             cls: "x-btn-icon x-grid-loading",
12815             handler: this.onClick.createDelegate(this, ["refresh"])
12816         });
12817
12818         if(this.displayInfo){
12819             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12820         }
12821     },
12822
12823     // private
12824     updateInfo : function(){
12825         if(this.displayEl){
12826             var count = this.ds.getCount();
12827             var msg = count == 0 ?
12828                 this.emptyMsg :
12829                 String.format(
12830                     this.displayMsg,
12831                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12832                 );
12833             this.displayEl.update(msg);
12834         }
12835     },
12836
12837     // private
12838     onLoad : function(ds, r, o){
12839        this.cursor = o.params ? o.params.start : 0;
12840        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12841
12842        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12843        this.field.dom.value = ap;
12844        this.first.setDisabled(ap == 1);
12845        this.prev.setDisabled(ap == 1);
12846        this.next.setDisabled(ap == ps);
12847        this.last.setDisabled(ap == ps);
12848        this.loading.enable();
12849        this.updateInfo();
12850     },
12851
12852     // private
12853     getPageData : function(){
12854         var total = this.ds.getTotalCount();
12855         return {
12856             total : total,
12857             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12858             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12859         };
12860     },
12861
12862     // private
12863     onLoadError : function(){
12864         this.loading.enable();
12865     },
12866
12867     // private
12868     onPagingKeydown : function(e){
12869         var k = e.getKey();
12870         var d = this.getPageData();
12871         if(k == e.RETURN){
12872             var v = this.field.dom.value, pageNum;
12873             if(!v || isNaN(pageNum = parseInt(v, 10))){
12874                 this.field.dom.value = d.activePage;
12875                 return;
12876             }
12877             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12878             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12879             e.stopEvent();
12880         }
12881         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))
12882         {
12883           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12884           this.field.dom.value = pageNum;
12885           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12886           e.stopEvent();
12887         }
12888         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12889         {
12890           var v = this.field.dom.value, pageNum; 
12891           var increment = (e.shiftKey) ? 10 : 1;
12892           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12893             increment *= -1;
12894           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12895             this.field.dom.value = d.activePage;
12896             return;
12897           }
12898           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12899           {
12900             this.field.dom.value = parseInt(v, 10) + increment;
12901             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12902             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12903           }
12904           e.stopEvent();
12905         }
12906     },
12907
12908     // private
12909     beforeLoad : function(){
12910         if(this.loading){
12911             this.loading.disable();
12912         }
12913     },
12914
12915     // private
12916     onClick : function(which){
12917         var ds = this.ds;
12918         switch(which){
12919             case "first":
12920                 ds.load({params:{start: 0, limit: this.pageSize}});
12921             break;
12922             case "prev":
12923                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12924             break;
12925             case "next":
12926                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12927             break;
12928             case "last":
12929                 var total = ds.getTotalCount();
12930                 var extra = total % this.pageSize;
12931                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12932                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12933             break;
12934             case "refresh":
12935                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12936             break;
12937         }
12938     },
12939
12940     /**
12941      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12942      * @param {Roo.data.Store} store The data store to unbind
12943      */
12944     unbind : function(ds){
12945         ds.un("beforeload", this.beforeLoad, this);
12946         ds.un("load", this.onLoad, this);
12947         ds.un("loadexception", this.onLoadError, this);
12948         ds.un("remove", this.updateInfo, this);
12949         ds.un("add", this.updateInfo, this);
12950         this.ds = undefined;
12951     },
12952
12953     /**
12954      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12955      * @param {Roo.data.Store} store The data store to bind
12956      */
12957     bind : function(ds){
12958         ds.on("beforeload", this.beforeLoad, this);
12959         ds.on("load", this.onLoad, this);
12960         ds.on("loadexception", this.onLoadError, this);
12961         ds.on("remove", this.updateInfo, this);
12962         ds.on("add", this.updateInfo, this);
12963         this.ds = ds;
12964     }
12965 });/*
12966  * Based on:
12967  * Ext JS Library 1.1.1
12968  * Copyright(c) 2006-2007, Ext JS, LLC.
12969  *
12970  * Originally Released Under LGPL - original licence link has changed is not relivant.
12971  *
12972  * Fork - LGPL
12973  * <script type="text/javascript">
12974  */
12975
12976 /**
12977  * @class Roo.Resizable
12978  * @extends Roo.util.Observable
12979  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12980  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12981  * 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
12982  * the element will be wrapped for you automatically.</p>
12983  * <p>Here is the list of valid resize handles:</p>
12984  * <pre>
12985 Value   Description
12986 ------  -------------------
12987  'n'     north
12988  's'     south
12989  'e'     east
12990  'w'     west
12991  'nw'    northwest
12992  'sw'    southwest
12993  'se'    southeast
12994  'ne'    northeast
12995  'all'   all
12996 </pre>
12997  * <p>Here's an example showing the creation of a typical Resizable:</p>
12998  * <pre><code>
12999 var resizer = new Roo.Resizable("element-id", {
13000     handles: 'all',
13001     minWidth: 200,
13002     minHeight: 100,
13003     maxWidth: 500,
13004     maxHeight: 400,
13005     pinned: true
13006 });
13007 resizer.on("resize", myHandler);
13008 </code></pre>
13009  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13010  * resizer.east.setDisplayed(false);</p>
13011  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13012  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13013  * resize operation's new size (defaults to [0, 0])
13014  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13015  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13016  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13017  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13018  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13019  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13020  * @cfg {Number} width The width of the element in pixels (defaults to null)
13021  * @cfg {Number} height The height of the element in pixels (defaults to null)
13022  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13023  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13024  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13025  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13026  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13027  * in favor of the handles config option (defaults to false)
13028  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13029  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13030  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13031  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13032  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13033  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13034  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13035  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13036  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13037  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13038  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13039  * @constructor
13040  * Create a new resizable component
13041  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13042  * @param {Object} config configuration options
13043   */
13044 Roo.Resizable = function(el, config){
13045     this.el = Roo.get(el);
13046
13047     if(config && config.wrap){
13048         config.resizeChild = this.el;
13049         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13050         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13051         this.el.setStyle("overflow", "hidden");
13052         this.el.setPositioning(config.resizeChild.getPositioning());
13053         config.resizeChild.clearPositioning();
13054         if(!config.width || !config.height){
13055             var csize = config.resizeChild.getSize();
13056             this.el.setSize(csize.width, csize.height);
13057         }
13058         if(config.pinned && !config.adjustments){
13059             config.adjustments = "auto";
13060         }
13061     }
13062
13063     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13064     this.proxy.unselectable();
13065     this.proxy.enableDisplayMode('block');
13066
13067     Roo.apply(this, config);
13068
13069     if(this.pinned){
13070         this.disableTrackOver = true;
13071         this.el.addClass("x-resizable-pinned");
13072     }
13073     // if the element isn't positioned, make it relative
13074     var position = this.el.getStyle("position");
13075     if(position != "absolute" && position != "fixed"){
13076         this.el.setStyle("position", "relative");
13077     }
13078     if(!this.handles){ // no handles passed, must be legacy style
13079         this.handles = 's,e,se';
13080         if(this.multiDirectional){
13081             this.handles += ',n,w';
13082         }
13083     }
13084     if(this.handles == "all"){
13085         this.handles = "n s e w ne nw se sw";
13086     }
13087     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13088     var ps = Roo.Resizable.positions;
13089     for(var i = 0, len = hs.length; i < len; i++){
13090         if(hs[i] && ps[hs[i]]){
13091             var pos = ps[hs[i]];
13092             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13093         }
13094     }
13095     // legacy
13096     this.corner = this.southeast;
13097
13098     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
13099         this.updateBox = true;
13100     }
13101
13102     this.activeHandle = null;
13103
13104     if(this.resizeChild){
13105         if(typeof this.resizeChild == "boolean"){
13106             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13107         }else{
13108             this.resizeChild = Roo.get(this.resizeChild, true);
13109         }
13110     }
13111
13112     if(this.adjustments == "auto"){
13113         var rc = this.resizeChild;
13114         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13115         if(rc && (hw || hn)){
13116             rc.position("relative");
13117             rc.setLeft(hw ? hw.el.getWidth() : 0);
13118             rc.setTop(hn ? hn.el.getHeight() : 0);
13119         }
13120         this.adjustments = [
13121             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13122             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13123         ];
13124     }
13125
13126     if(this.draggable){
13127         this.dd = this.dynamic ?
13128             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13129         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13130     }
13131
13132     // public events
13133     this.addEvents({
13134         /**
13135          * @event beforeresize
13136          * Fired before resize is allowed. Set enabled to false to cancel resize.
13137          * @param {Roo.Resizable} this
13138          * @param {Roo.EventObject} e The mousedown event
13139          */
13140         "beforeresize" : true,
13141         /**
13142          * @event resize
13143          * Fired after a resize.
13144          * @param {Roo.Resizable} this
13145          * @param {Number} width The new width
13146          * @param {Number} height The new height
13147          * @param {Roo.EventObject} e The mouseup event
13148          */
13149         "resize" : true
13150     });
13151
13152     if(this.width !== null && this.height !== null){
13153         this.resizeTo(this.width, this.height);
13154     }else{
13155         this.updateChildSize();
13156     }
13157     if(Roo.isIE){
13158         this.el.dom.style.zoom = 1;
13159     }
13160     Roo.Resizable.superclass.constructor.call(this);
13161 };
13162
13163 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13164         resizeChild : false,
13165         adjustments : [0, 0],
13166         minWidth : 5,
13167         minHeight : 5,
13168         maxWidth : 10000,
13169         maxHeight : 10000,
13170         enabled : true,
13171         animate : false,
13172         duration : .35,
13173         dynamic : false,
13174         handles : false,
13175         multiDirectional : false,
13176         disableTrackOver : false,
13177         easing : 'easeOutStrong',
13178         widthIncrement : 0,
13179         heightIncrement : 0,
13180         pinned : false,
13181         width : null,
13182         height : null,
13183         preserveRatio : false,
13184         transparent: false,
13185         minX: 0,
13186         minY: 0,
13187         draggable: false,
13188
13189         /**
13190          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13191          */
13192         constrainTo: undefined,
13193         /**
13194          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13195          */
13196         resizeRegion: undefined,
13197
13198
13199     /**
13200      * Perform a manual resize
13201      * @param {Number} width
13202      * @param {Number} height
13203      */
13204     resizeTo : function(width, height){
13205         this.el.setSize(width, height);
13206         this.updateChildSize();
13207         this.fireEvent("resize", this, width, height, null);
13208     },
13209
13210     // private
13211     startSizing : function(e, handle){
13212         this.fireEvent("beforeresize", this, e);
13213         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13214
13215             if(!this.overlay){
13216                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13217                 this.overlay.unselectable();
13218                 this.overlay.enableDisplayMode("block");
13219                 this.overlay.on("mousemove", this.onMouseMove, this);
13220                 this.overlay.on("mouseup", this.onMouseUp, this);
13221             }
13222             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13223
13224             this.resizing = true;
13225             this.startBox = this.el.getBox();
13226             this.startPoint = e.getXY();
13227             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13228                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13229
13230             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13231             this.overlay.show();
13232
13233             if(this.constrainTo) {
13234                 var ct = Roo.get(this.constrainTo);
13235                 this.resizeRegion = ct.getRegion().adjust(
13236                     ct.getFrameWidth('t'),
13237                     ct.getFrameWidth('l'),
13238                     -ct.getFrameWidth('b'),
13239                     -ct.getFrameWidth('r')
13240                 );
13241             }
13242
13243             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13244             this.proxy.show();
13245             this.proxy.setBox(this.startBox);
13246             if(!this.dynamic){
13247                 this.proxy.setStyle('visibility', 'visible');
13248             }
13249         }
13250     },
13251
13252     // private
13253     onMouseDown : function(handle, e){
13254         if(this.enabled){
13255             e.stopEvent();
13256             this.activeHandle = handle;
13257             this.startSizing(e, handle);
13258         }
13259     },
13260
13261     // private
13262     onMouseUp : function(e){
13263         var size = this.resizeElement();
13264         this.resizing = false;
13265         this.handleOut();
13266         this.overlay.hide();
13267         this.proxy.hide();
13268         this.fireEvent("resize", this, size.width, size.height, e);
13269     },
13270
13271     // private
13272     updateChildSize : function(){
13273         if(this.resizeChild){
13274             var el = this.el;
13275             var child = this.resizeChild;
13276             var adj = this.adjustments;
13277             if(el.dom.offsetWidth){
13278                 var b = el.getSize(true);
13279                 child.setSize(b.width+adj[0], b.height+adj[1]);
13280             }
13281             // Second call here for IE
13282             // The first call enables instant resizing and
13283             // the second call corrects scroll bars if they
13284             // exist
13285             if(Roo.isIE){
13286                 setTimeout(function(){
13287                     if(el.dom.offsetWidth){
13288                         var b = el.getSize(true);
13289                         child.setSize(b.width+adj[0], b.height+adj[1]);
13290                     }
13291                 }, 10);
13292             }
13293         }
13294     },
13295
13296     // private
13297     snap : function(value, inc, min){
13298         if(!inc || !value) return value;
13299         var newValue = value;
13300         var m = value % inc;
13301         if(m > 0){
13302             if(m > (inc/2)){
13303                 newValue = value + (inc-m);
13304             }else{
13305                 newValue = value - m;
13306             }
13307         }
13308         return Math.max(min, newValue);
13309     },
13310
13311     // private
13312     resizeElement : function(){
13313         var box = this.proxy.getBox();
13314         if(this.updateBox){
13315             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13316         }else{
13317             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13318         }
13319         this.updateChildSize();
13320         if(!this.dynamic){
13321             this.proxy.hide();
13322         }
13323         return box;
13324     },
13325
13326     // private
13327     constrain : function(v, diff, m, mx){
13328         if(v - diff < m){
13329             diff = v - m;
13330         }else if(v - diff > mx){
13331             diff = mx - v;
13332         }
13333         return diff;
13334     },
13335
13336     // private
13337     onMouseMove : function(e){
13338         if(this.enabled){
13339             try{// try catch so if something goes wrong the user doesn't get hung
13340
13341             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13342                 return;
13343             }
13344
13345             //var curXY = this.startPoint;
13346             var curSize = this.curSize || this.startBox;
13347             var x = this.startBox.x, y = this.startBox.y;
13348             var ox = x, oy = y;
13349             var w = curSize.width, h = curSize.height;
13350             var ow = w, oh = h;
13351             var mw = this.minWidth, mh = this.minHeight;
13352             var mxw = this.maxWidth, mxh = this.maxHeight;
13353             var wi = this.widthIncrement;
13354             var hi = this.heightIncrement;
13355
13356             var eventXY = e.getXY();
13357             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13358             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13359
13360             var pos = this.activeHandle.position;
13361
13362             switch(pos){
13363                 case "east":
13364                     w += diffX;
13365                     w = Math.min(Math.max(mw, w), mxw);
13366                     break;
13367                 case "south":
13368                     h += diffY;
13369                     h = Math.min(Math.max(mh, h), mxh);
13370                     break;
13371                 case "southeast":
13372                     w += diffX;
13373                     h += diffY;
13374                     w = Math.min(Math.max(mw, w), mxw);
13375                     h = Math.min(Math.max(mh, h), mxh);
13376                     break;
13377                 case "north":
13378                     diffY = this.constrain(h, diffY, mh, mxh);
13379                     y += diffY;
13380                     h -= diffY;
13381                     break;
13382                 case "west":
13383                     diffX = this.constrain(w, diffX, mw, mxw);
13384                     x += diffX;
13385                     w -= diffX;
13386                     break;
13387                 case "northeast":
13388                     w += diffX;
13389                     w = Math.min(Math.max(mw, w), mxw);
13390                     diffY = this.constrain(h, diffY, mh, mxh);
13391                     y += diffY;
13392                     h -= diffY;
13393                     break;
13394                 case "northwest":
13395                     diffX = this.constrain(w, diffX, mw, mxw);
13396                     diffY = this.constrain(h, diffY, mh, mxh);
13397                     y += diffY;
13398                     h -= diffY;
13399                     x += diffX;
13400                     w -= diffX;
13401                     break;
13402                case "southwest":
13403                     diffX = this.constrain(w, diffX, mw, mxw);
13404                     h += diffY;
13405                     h = Math.min(Math.max(mh, h), mxh);
13406                     x += diffX;
13407                     w -= diffX;
13408                     break;
13409             }
13410
13411             var sw = this.snap(w, wi, mw);
13412             var sh = this.snap(h, hi, mh);
13413             if(sw != w || sh != h){
13414                 switch(pos){
13415                     case "northeast":
13416                         y -= sh - h;
13417                     break;
13418                     case "north":
13419                         y -= sh - h;
13420                         break;
13421                     case "southwest":
13422                         x -= sw - w;
13423                     break;
13424                     case "west":
13425                         x -= sw - w;
13426                         break;
13427                     case "northwest":
13428                         x -= sw - w;
13429                         y -= sh - h;
13430                     break;
13431                 }
13432                 w = sw;
13433                 h = sh;
13434             }
13435
13436             if(this.preserveRatio){
13437                 switch(pos){
13438                     case "southeast":
13439                     case "east":
13440                         h = oh * (w/ow);
13441                         h = Math.min(Math.max(mh, h), mxh);
13442                         w = ow * (h/oh);
13443                        break;
13444                     case "south":
13445                         w = ow * (h/oh);
13446                         w = Math.min(Math.max(mw, w), mxw);
13447                         h = oh * (w/ow);
13448                         break;
13449                     case "northeast":
13450                         w = ow * (h/oh);
13451                         w = Math.min(Math.max(mw, w), mxw);
13452                         h = oh * (w/ow);
13453                     break;
13454                     case "north":
13455                         var tw = w;
13456                         w = ow * (h/oh);
13457                         w = Math.min(Math.max(mw, w), mxw);
13458                         h = oh * (w/ow);
13459                         x += (tw - w) / 2;
13460                         break;
13461                     case "southwest":
13462                         h = oh * (w/ow);
13463                         h = Math.min(Math.max(mh, h), mxh);
13464                         var tw = w;
13465                         w = ow * (h/oh);
13466                         x += tw - w;
13467                         break;
13468                     case "west":
13469                         var th = h;
13470                         h = oh * (w/ow);
13471                         h = Math.min(Math.max(mh, h), mxh);
13472                         y += (th - h) / 2;
13473                         var tw = w;
13474                         w = ow * (h/oh);
13475                         x += tw - w;
13476                        break;
13477                     case "northwest":
13478                         var tw = w;
13479                         var th = h;
13480                         h = oh * (w/ow);
13481                         h = Math.min(Math.max(mh, h), mxh);
13482                         w = ow * (h/oh);
13483                         y += th - h;
13484                          x += tw - w;
13485                        break;
13486
13487                 }
13488             }
13489             this.proxy.setBounds(x, y, w, h);
13490             if(this.dynamic){
13491                 this.resizeElement();
13492             }
13493             }catch(e){}
13494         }
13495     },
13496
13497     // private
13498     handleOver : function(){
13499         if(this.enabled){
13500             this.el.addClass("x-resizable-over");
13501         }
13502     },
13503
13504     // private
13505     handleOut : function(){
13506         if(!this.resizing){
13507             this.el.removeClass("x-resizable-over");
13508         }
13509     },
13510
13511     /**
13512      * Returns the element this component is bound to.
13513      * @return {Roo.Element}
13514      */
13515     getEl : function(){
13516         return this.el;
13517     },
13518
13519     /**
13520      * Returns the resizeChild element (or null).
13521      * @return {Roo.Element}
13522      */
13523     getResizeChild : function(){
13524         return this.resizeChild;
13525     },
13526
13527     /**
13528      * Destroys this resizable. If the element was wrapped and
13529      * removeEl is not true then the element remains.
13530      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13531      */
13532     destroy : function(removeEl){
13533         this.proxy.remove();
13534         if(this.overlay){
13535             this.overlay.removeAllListeners();
13536             this.overlay.remove();
13537         }
13538         var ps = Roo.Resizable.positions;
13539         for(var k in ps){
13540             if(typeof ps[k] != "function" && this[ps[k]]){
13541                 var h = this[ps[k]];
13542                 h.el.removeAllListeners();
13543                 h.el.remove();
13544             }
13545         }
13546         if(removeEl){
13547             this.el.update("");
13548             this.el.remove();
13549         }
13550     }
13551 });
13552
13553 // private
13554 // hash to map config positions to true positions
13555 Roo.Resizable.positions = {
13556     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
13557 };
13558
13559 // private
13560 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13561     if(!this.tpl){
13562         // only initialize the template if resizable is used
13563         var tpl = Roo.DomHelper.createTemplate(
13564             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13565         );
13566         tpl.compile();
13567         Roo.Resizable.Handle.prototype.tpl = tpl;
13568     }
13569     this.position = pos;
13570     this.rz = rz;
13571     this.el = this.tpl.append(rz.el.dom, [this.position], true);
13572     this.el.unselectable();
13573     if(transparent){
13574         this.el.setOpacity(0);
13575     }
13576     this.el.on("mousedown", this.onMouseDown, this);
13577     if(!disableTrackOver){
13578         this.el.on("mouseover", this.onMouseOver, this);
13579         this.el.on("mouseout", this.onMouseOut, this);
13580     }
13581 };
13582
13583 // private
13584 Roo.Resizable.Handle.prototype = {
13585     afterResize : function(rz){
13586         // do nothing
13587     },
13588     // private
13589     onMouseDown : function(e){
13590         this.rz.onMouseDown(this, e);
13591     },
13592     // private
13593     onMouseOver : function(e){
13594         this.rz.handleOver(this, e);
13595     },
13596     // private
13597     onMouseOut : function(e){
13598         this.rz.handleOut(this, e);
13599     }
13600 };/*
13601  * Based on:
13602  * Ext JS Library 1.1.1
13603  * Copyright(c) 2006-2007, Ext JS, LLC.
13604  *
13605  * Originally Released Under LGPL - original licence link has changed is not relivant.
13606  *
13607  * Fork - LGPL
13608  * <script type="text/javascript">
13609  */
13610
13611 /**
13612  * @class Roo.Editor
13613  * @extends Roo.Component
13614  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13615  * @constructor
13616  * Create a new Editor
13617  * @param {Roo.form.Field} field The Field object (or descendant)
13618  * @param {Object} config The config object
13619  */
13620 Roo.Editor = function(field, config){
13621     Roo.Editor.superclass.constructor.call(this, config);
13622     this.field = field;
13623     this.addEvents({
13624         /**
13625              * @event beforestartedit
13626              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13627              * false from the handler of this event.
13628              * @param {Editor} this
13629              * @param {Roo.Element} boundEl The underlying element bound to this editor
13630              * @param {Mixed} value The field value being set
13631              */
13632         "beforestartedit" : true,
13633         /**
13634              * @event startedit
13635              * Fires when this editor is displayed
13636              * @param {Roo.Element} boundEl The underlying element bound to this editor
13637              * @param {Mixed} value The starting field value
13638              */
13639         "startedit" : true,
13640         /**
13641              * @event beforecomplete
13642              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13643              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13644              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13645              * event will not fire since no edit actually occurred.
13646              * @param {Editor} this
13647              * @param {Mixed} value The current field value
13648              * @param {Mixed} startValue The original field value
13649              */
13650         "beforecomplete" : true,
13651         /**
13652              * @event complete
13653              * Fires after editing is complete and any changed value has been written to the underlying field.
13654              * @param {Editor} this
13655              * @param {Mixed} value The current field value
13656              * @param {Mixed} startValue The original field value
13657              */
13658         "complete" : true,
13659         /**
13660          * @event specialkey
13661          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13662          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13663          * @param {Roo.form.Field} this
13664          * @param {Roo.EventObject} e The event object
13665          */
13666         "specialkey" : true
13667     });
13668 };
13669
13670 Roo.extend(Roo.Editor, Roo.Component, {
13671     /**
13672      * @cfg {Boolean/String} autosize
13673      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13674      * or "height" to adopt the height only (defaults to false)
13675      */
13676     /**
13677      * @cfg {Boolean} revertInvalid
13678      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13679      * validation fails (defaults to true)
13680      */
13681     /**
13682      * @cfg {Boolean} ignoreNoChange
13683      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13684      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13685      * will never be ignored.
13686      */
13687     /**
13688      * @cfg {Boolean} hideEl
13689      * False to keep the bound element visible while the editor is displayed (defaults to true)
13690      */
13691     /**
13692      * @cfg {Mixed} value
13693      * The data value of the underlying field (defaults to "")
13694      */
13695     value : "",
13696     /**
13697      * @cfg {String} alignment
13698      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13699      */
13700     alignment: "c-c?",
13701     /**
13702      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13703      * for bottom-right shadow (defaults to "frame")
13704      */
13705     shadow : "frame",
13706     /**
13707      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13708      */
13709     constrain : false,
13710     /**
13711      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13712      */
13713     completeOnEnter : false,
13714     /**
13715      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13716      */
13717     cancelOnEsc : false,
13718     /**
13719      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13720      */
13721     updateEl : false,
13722
13723     // private
13724     onRender : function(ct, position){
13725         this.el = new Roo.Layer({
13726             shadow: this.shadow,
13727             cls: "x-editor",
13728             parentEl : ct,
13729             shim : this.shim,
13730             shadowOffset:4,
13731             id: this.id,
13732             constrain: this.constrain
13733         });
13734         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13735         if(this.field.msgTarget != 'title'){
13736             this.field.msgTarget = 'qtip';
13737         }
13738         this.field.render(this.el);
13739         if(Roo.isGecko){
13740             this.field.el.dom.setAttribute('autocomplete', 'off');
13741         }
13742         this.field.on("specialkey", this.onSpecialKey, this);
13743         if(this.swallowKeys){
13744             this.field.el.swallowEvent(['keydown','keypress']);
13745         }
13746         this.field.show();
13747         this.field.on("blur", this.onBlur, this);
13748         if(this.field.grow){
13749             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13750         }
13751     },
13752
13753     onSpecialKey : function(field, e){
13754         if(this.completeOnEnter && e.getKey() == e.ENTER){
13755             e.stopEvent();
13756             this.completeEdit();
13757         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13758             this.cancelEdit();
13759         }else{
13760             this.fireEvent('specialkey', field, e);
13761         }
13762     },
13763
13764     /**
13765      * Starts the editing process and shows the editor.
13766      * @param {String/HTMLElement/Element} el The element to edit
13767      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13768       * to the innerHTML of el.
13769      */
13770     startEdit : function(el, value){
13771         if(this.editing){
13772             this.completeEdit();
13773         }
13774         this.boundEl = Roo.get(el);
13775         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13776         if(!this.rendered){
13777             this.render(this.parentEl || document.body);
13778         }
13779         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13780             return;
13781         }
13782         this.startValue = v;
13783         this.field.setValue(v);
13784         if(this.autoSize){
13785             var sz = this.boundEl.getSize();
13786             switch(this.autoSize){
13787                 case "width":
13788                 this.setSize(sz.width,  "");
13789                 break;
13790                 case "height":
13791                 this.setSize("",  sz.height);
13792                 break;
13793                 default:
13794                 this.setSize(sz.width,  sz.height);
13795             }
13796         }
13797         this.el.alignTo(this.boundEl, this.alignment);
13798         this.editing = true;
13799         if(Roo.QuickTips){
13800             Roo.QuickTips.disable();
13801         }
13802         this.show();
13803     },
13804
13805     /**
13806      * Sets the height and width of this editor.
13807      * @param {Number} width The new width
13808      * @param {Number} height The new height
13809      */
13810     setSize : function(w, h){
13811         this.field.setSize(w, h);
13812         if(this.el){
13813             this.el.sync();
13814         }
13815     },
13816
13817     /**
13818      * Realigns the editor to the bound field based on the current alignment config value.
13819      */
13820     realign : function(){
13821         this.el.alignTo(this.boundEl, this.alignment);
13822     },
13823
13824     /**
13825      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13826      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13827      */
13828     completeEdit : function(remainVisible){
13829         if(!this.editing){
13830             return;
13831         }
13832         var v = this.getValue();
13833         if(this.revertInvalid !== false && !this.field.isValid()){
13834             v = this.startValue;
13835             this.cancelEdit(true);
13836         }
13837         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13838             this.editing = false;
13839             this.hide();
13840             return;
13841         }
13842         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13843             this.editing = false;
13844             if(this.updateEl && this.boundEl){
13845                 this.boundEl.update(v);
13846             }
13847             if(remainVisible !== true){
13848                 this.hide();
13849             }
13850             this.fireEvent("complete", this, v, this.startValue);
13851         }
13852     },
13853
13854     // private
13855     onShow : function(){
13856         this.el.show();
13857         if(this.hideEl !== false){
13858             this.boundEl.hide();
13859         }
13860         this.field.show();
13861         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13862             this.fixIEFocus = true;
13863             this.deferredFocus.defer(50, this);
13864         }else{
13865             this.field.focus();
13866         }
13867         this.fireEvent("startedit", this.boundEl, this.startValue);
13868     },
13869
13870     deferredFocus : function(){
13871         if(this.editing){
13872             this.field.focus();
13873         }
13874     },
13875
13876     /**
13877      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13878      * reverted to the original starting value.
13879      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13880      * cancel (defaults to false)
13881      */
13882     cancelEdit : function(remainVisible){
13883         if(this.editing){
13884             this.setValue(this.startValue);
13885             if(remainVisible !== true){
13886                 this.hide();
13887             }
13888         }
13889     },
13890
13891     // private
13892     onBlur : function(){
13893         if(this.allowBlur !== true && this.editing){
13894             this.completeEdit();
13895         }
13896     },
13897
13898     // private
13899     onHide : function(){
13900         if(this.editing){
13901             this.completeEdit();
13902             return;
13903         }
13904         this.field.blur();
13905         if(this.field.collapse){
13906             this.field.collapse();
13907         }
13908         this.el.hide();
13909         if(this.hideEl !== false){
13910             this.boundEl.show();
13911         }
13912         if(Roo.QuickTips){
13913             Roo.QuickTips.enable();
13914         }
13915     },
13916
13917     /**
13918      * Sets the data value of the editor
13919      * @param {Mixed} value Any valid value supported by the underlying field
13920      */
13921     setValue : function(v){
13922         this.field.setValue(v);
13923     },
13924
13925     /**
13926      * Gets the data value of the editor
13927      * @return {Mixed} The data value
13928      */
13929     getValue : function(){
13930         return this.field.getValue();
13931     }
13932 });/*
13933  * Based on:
13934  * Ext JS Library 1.1.1
13935  * Copyright(c) 2006-2007, Ext JS, LLC.
13936  *
13937  * Originally Released Under LGPL - original licence link has changed is not relivant.
13938  *
13939  * Fork - LGPL
13940  * <script type="text/javascript">
13941  */
13942  
13943 /**
13944  * @class Roo.BasicDialog
13945  * @extends Roo.util.Observable
13946  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13947  * <pre><code>
13948 var dlg = new Roo.BasicDialog("my-dlg", {
13949     height: 200,
13950     width: 300,
13951     minHeight: 100,
13952     minWidth: 150,
13953     modal: true,
13954     proxyDrag: true,
13955     shadow: true
13956 });
13957 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13958 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13959 dlg.addButton('Cancel', dlg.hide, dlg);
13960 dlg.show();
13961 </code></pre>
13962   <b>A Dialog should always be a direct child of the body element.</b>
13963  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13964  * @cfg {String} title Default text to display in the title bar (defaults to null)
13965  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13966  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13967  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13968  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13969  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13970  * (defaults to null with no animation)
13971  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13972  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13973  * property for valid values (defaults to 'all')
13974  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13975  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13976  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13977  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13978  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13979  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13980  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13981  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13982  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13983  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13984  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13985  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13986  * draggable = true (defaults to false)
13987  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13988  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13989  * shadow (defaults to false)
13990  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13991  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13992  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13993  * @cfg {Array} buttons Array of buttons
13994  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13995  * @constructor
13996  * Create a new BasicDialog.
13997  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13998  * @param {Object} config Configuration options
13999  */
14000 Roo.BasicDialog = function(el, config){
14001     this.el = Roo.get(el);
14002     var dh = Roo.DomHelper;
14003     if(!this.el && config && config.autoCreate){
14004         if(typeof config.autoCreate == "object"){
14005             if(!config.autoCreate.id){
14006                 config.autoCreate.id = el;
14007             }
14008             this.el = dh.append(document.body,
14009                         config.autoCreate, true);
14010         }else{
14011             this.el = dh.append(document.body,
14012                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14013         }
14014     }
14015     el = this.el;
14016     el.setDisplayed(true);
14017     el.hide = this.hideAction;
14018     this.id = el.id;
14019     el.addClass("x-dlg");
14020
14021     Roo.apply(this, config);
14022
14023     this.proxy = el.createProxy("x-dlg-proxy");
14024     this.proxy.hide = this.hideAction;
14025     this.proxy.setOpacity(.5);
14026     this.proxy.hide();
14027
14028     if(config.width){
14029         el.setWidth(config.width);
14030     }
14031     if(config.height){
14032         el.setHeight(config.height);
14033     }
14034     this.size = el.getSize();
14035     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14036         this.xy = [config.x,config.y];
14037     }else{
14038         this.xy = el.getCenterXY(true);
14039     }
14040     /** The header element @type Roo.Element */
14041     this.header = el.child("> .x-dlg-hd");
14042     /** The body element @type Roo.Element */
14043     this.body = el.child("> .x-dlg-bd");
14044     /** The footer element @type Roo.Element */
14045     this.footer = el.child("> .x-dlg-ft");
14046
14047     if(!this.header){
14048         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14049     }
14050     if(!this.body){
14051         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14052     }
14053
14054     this.header.unselectable();
14055     if(this.title){
14056         this.header.update(this.title);
14057     }
14058     // this element allows the dialog to be focused for keyboard event
14059     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14060     this.focusEl.swallowEvent("click", true);
14061
14062     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14063
14064     // wrap the body and footer for special rendering
14065     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14066     if(this.footer){
14067         this.bwrap.dom.appendChild(this.footer.dom);
14068     }
14069
14070     this.bg = this.el.createChild({
14071         tag: "div", cls:"x-dlg-bg",
14072         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14073     });
14074     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14075
14076
14077     if(this.autoScroll !== false && !this.autoTabs){
14078         this.body.setStyle("overflow", "auto");
14079     }
14080
14081     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14082
14083     if(this.closable !== false){
14084         this.el.addClass("x-dlg-closable");
14085         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14086         this.close.on("click", this.closeClick, this);
14087         this.close.addClassOnOver("x-dlg-close-over");
14088     }
14089     if(this.collapsible !== false){
14090         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14091         this.collapseBtn.on("click", this.collapseClick, this);
14092         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14093         this.header.on("dblclick", this.collapseClick, this);
14094     }
14095     if(this.resizable !== false){
14096         this.el.addClass("x-dlg-resizable");
14097         this.resizer = new Roo.Resizable(el, {
14098             minWidth: this.minWidth || 80,
14099             minHeight:this.minHeight || 80,
14100             handles: this.resizeHandles || "all",
14101             pinned: true
14102         });
14103         this.resizer.on("beforeresize", this.beforeResize, this);
14104         this.resizer.on("resize", this.onResize, this);
14105     }
14106     if(this.draggable !== false){
14107         el.addClass("x-dlg-draggable");
14108         if (!this.proxyDrag) {
14109             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14110         }
14111         else {
14112             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14113         }
14114         dd.setHandleElId(this.header.id);
14115         dd.endDrag = this.endMove.createDelegate(this);
14116         dd.startDrag = this.startMove.createDelegate(this);
14117         dd.onDrag = this.onDrag.createDelegate(this);
14118         dd.scroll = false;
14119         this.dd = dd;
14120     }
14121     if(this.modal){
14122         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14123         this.mask.enableDisplayMode("block");
14124         this.mask.hide();
14125         this.el.addClass("x-dlg-modal");
14126     }
14127     if(this.shadow){
14128         this.shadow = new Roo.Shadow({
14129             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14130             offset : this.shadowOffset
14131         });
14132     }else{
14133         this.shadowOffset = 0;
14134     }
14135     if(Roo.useShims && this.shim !== false){
14136         this.shim = this.el.createShim();
14137         this.shim.hide = this.hideAction;
14138         this.shim.hide();
14139     }else{
14140         this.shim = false;
14141     }
14142     if(this.autoTabs){
14143         this.initTabs();
14144     }
14145     if (this.buttons) { 
14146         var bts= this.buttons;
14147         this.buttons = [];
14148         Roo.each(bts, function(b) {
14149             this.addButton(b);
14150         }, this);
14151     }
14152     
14153     
14154     this.addEvents({
14155         /**
14156          * @event keydown
14157          * Fires when a key is pressed
14158          * @param {Roo.BasicDialog} this
14159          * @param {Roo.EventObject} e
14160          */
14161         "keydown" : true,
14162         /**
14163          * @event move
14164          * Fires when this dialog is moved by the user.
14165          * @param {Roo.BasicDialog} this
14166          * @param {Number} x The new page X
14167          * @param {Number} y The new page Y
14168          */
14169         "move" : true,
14170         /**
14171          * @event resize
14172          * Fires when this dialog is resized by the user.
14173          * @param {Roo.BasicDialog} this
14174          * @param {Number} width The new width
14175          * @param {Number} height The new height
14176          */
14177         "resize" : true,
14178         /**
14179          * @event beforehide
14180          * Fires before this dialog is hidden.
14181          * @param {Roo.BasicDialog} this
14182          */
14183         "beforehide" : true,
14184         /**
14185          * @event hide
14186          * Fires when this dialog is hidden.
14187          * @param {Roo.BasicDialog} this
14188          */
14189         "hide" : true,
14190         /**
14191          * @event beforeshow
14192          * Fires before this dialog is shown.
14193          * @param {Roo.BasicDialog} this
14194          */
14195         "beforeshow" : true,
14196         /**
14197          * @event show
14198          * Fires when this dialog is shown.
14199          * @param {Roo.BasicDialog} this
14200          */
14201         "show" : true
14202     });
14203     el.on("keydown", this.onKeyDown, this);
14204     el.on("mousedown", this.toFront, this);
14205     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14206     this.el.hide();
14207     Roo.DialogManager.register(this);
14208     Roo.BasicDialog.superclass.constructor.call(this);
14209 };
14210
14211 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14212     shadowOffset: Roo.isIE ? 6 : 5,
14213     minHeight: 80,
14214     minWidth: 200,
14215     minButtonWidth: 75,
14216     defaultButton: null,
14217     buttonAlign: "right",
14218     tabTag: 'div',
14219     firstShow: true,
14220
14221     /**
14222      * Sets the dialog title text
14223      * @param {String} text The title text to display
14224      * @return {Roo.BasicDialog} this
14225      */
14226     setTitle : function(text){
14227         this.header.update(text);
14228         return this;
14229     },
14230
14231     // private
14232     closeClick : function(){
14233         this.hide();
14234     },
14235
14236     // private
14237     collapseClick : function(){
14238         this[this.collapsed ? "expand" : "collapse"]();
14239     },
14240
14241     /**
14242      * Collapses the dialog to its minimized state (only the title bar is visible).
14243      * Equivalent to the user clicking the collapse dialog button.
14244      */
14245     collapse : function(){
14246         if(!this.collapsed){
14247             this.collapsed = true;
14248             this.el.addClass("x-dlg-collapsed");
14249             this.restoreHeight = this.el.getHeight();
14250             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14251         }
14252     },
14253
14254     /**
14255      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14256      * clicking the expand dialog button.
14257      */
14258     expand : function(){
14259         if(this.collapsed){
14260             this.collapsed = false;
14261             this.el.removeClass("x-dlg-collapsed");
14262             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14263         }
14264     },
14265
14266     /**
14267      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14268      * @return {Roo.TabPanel} The tabs component
14269      */
14270     initTabs : function(){
14271         var tabs = this.getTabs();
14272         while(tabs.getTab(0)){
14273             tabs.removeTab(0);
14274         }
14275         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14276             var dom = el.dom;
14277             tabs.addTab(Roo.id(dom), dom.title);
14278             dom.title = "";
14279         });
14280         tabs.activate(0);
14281         return tabs;
14282     },
14283
14284     // private
14285     beforeResize : function(){
14286         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14287     },
14288
14289     // private
14290     onResize : function(){
14291         this.refreshSize();
14292         this.syncBodyHeight();
14293         this.adjustAssets();
14294         this.focus();
14295         this.fireEvent("resize", this, this.size.width, this.size.height);
14296     },
14297
14298     // private
14299     onKeyDown : function(e){
14300         if(this.isVisible()){
14301             this.fireEvent("keydown", this, e);
14302         }
14303     },
14304
14305     /**
14306      * Resizes the dialog.
14307      * @param {Number} width
14308      * @param {Number} height
14309      * @return {Roo.BasicDialog} this
14310      */
14311     resizeTo : function(width, height){
14312         this.el.setSize(width, height);
14313         this.size = {width: width, height: height};
14314         this.syncBodyHeight();
14315         if(this.fixedcenter){
14316             this.center();
14317         }
14318         if(this.isVisible()){
14319             this.constrainXY();
14320             this.adjustAssets();
14321         }
14322         this.fireEvent("resize", this, width, height);
14323         return this;
14324     },
14325
14326
14327     /**
14328      * Resizes the dialog to fit the specified content size.
14329      * @param {Number} width
14330      * @param {Number} height
14331      * @return {Roo.BasicDialog} this
14332      */
14333     setContentSize : function(w, h){
14334         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14335         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14336         //if(!this.el.isBorderBox()){
14337             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14338             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14339         //}
14340         if(this.tabs){
14341             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14342             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14343         }
14344         this.resizeTo(w, h);
14345         return this;
14346     },
14347
14348     /**
14349      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14350      * executed in response to a particular key being pressed while the dialog is active.
14351      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14352      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14353      * @param {Function} fn The function to call
14354      * @param {Object} scope (optional) The scope of the function
14355      * @return {Roo.BasicDialog} this
14356      */
14357     addKeyListener : function(key, fn, scope){
14358         var keyCode, shift, ctrl, alt;
14359         if(typeof key == "object" && !(key instanceof Array)){
14360             keyCode = key["key"];
14361             shift = key["shift"];
14362             ctrl = key["ctrl"];
14363             alt = key["alt"];
14364         }else{
14365             keyCode = key;
14366         }
14367         var handler = function(dlg, e){
14368             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14369                 var k = e.getKey();
14370                 if(keyCode instanceof Array){
14371                     for(var i = 0, len = keyCode.length; i < len; i++){
14372                         if(keyCode[i] == k){
14373                           fn.call(scope || window, dlg, k, e);
14374                           return;
14375                         }
14376                     }
14377                 }else{
14378                     if(k == keyCode){
14379                         fn.call(scope || window, dlg, k, e);
14380                     }
14381                 }
14382             }
14383         };
14384         this.on("keydown", handler);
14385         return this;
14386     },
14387
14388     /**
14389      * Returns the TabPanel component (creates it if it doesn't exist).
14390      * Note: If you wish to simply check for the existence of tabs without creating them,
14391      * check for a null 'tabs' property.
14392      * @return {Roo.TabPanel} The tabs component
14393      */
14394     getTabs : function(){
14395         if(!this.tabs){
14396             this.el.addClass("x-dlg-auto-tabs");
14397             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14398             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14399         }
14400         return this.tabs;
14401     },
14402
14403     /**
14404      * Adds a button to the footer section of the dialog.
14405      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14406      * object or a valid Roo.DomHelper element config
14407      * @param {Function} handler The function called when the button is clicked
14408      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14409      * @return {Roo.Button} The new button
14410      */
14411     addButton : function(config, handler, scope){
14412         var dh = Roo.DomHelper;
14413         if(!this.footer){
14414             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14415         }
14416         if(!this.btnContainer){
14417             var tb = this.footer.createChild({
14418
14419                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14420                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14421             }, null, true);
14422             this.btnContainer = tb.firstChild.firstChild.firstChild;
14423         }
14424         var bconfig = {
14425             handler: handler,
14426             scope: scope,
14427             minWidth: this.minButtonWidth,
14428             hideParent:true
14429         };
14430         if(typeof config == "string"){
14431             bconfig.text = config;
14432         }else{
14433             if(config.tag){
14434                 bconfig.dhconfig = config;
14435             }else{
14436                 Roo.apply(bconfig, config);
14437             }
14438         }
14439         var fc = false;
14440         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14441             bconfig.position = Math.max(0, bconfig.position);
14442             fc = this.btnContainer.childNodes[bconfig.position];
14443         }
14444          
14445         var btn = new Roo.Button(
14446             fc ? 
14447                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14448                 : this.btnContainer.appendChild(document.createElement("td")),
14449             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14450             bconfig
14451         );
14452         this.syncBodyHeight();
14453         if(!this.buttons){
14454             /**
14455              * Array of all the buttons that have been added to this dialog via addButton
14456              * @type Array
14457              */
14458             this.buttons = [];
14459         }
14460         this.buttons.push(btn);
14461         return btn;
14462     },
14463
14464     /**
14465      * Sets the default button to be focused when the dialog is displayed.
14466      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14467      * @return {Roo.BasicDialog} this
14468      */
14469     setDefaultButton : function(btn){
14470         this.defaultButton = btn;
14471         return this;
14472     },
14473
14474     // private
14475     getHeaderFooterHeight : function(safe){
14476         var height = 0;
14477         if(this.header){
14478            height += this.header.getHeight();
14479         }
14480         if(this.footer){
14481            var fm = this.footer.getMargins();
14482             height += (this.footer.getHeight()+fm.top+fm.bottom);
14483         }
14484         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14485         height += this.centerBg.getPadding("tb");
14486         return height;
14487     },
14488
14489     // private
14490     syncBodyHeight : function(){
14491         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14492         var height = this.size.height - this.getHeaderFooterHeight(false);
14493         bd.setHeight(height-bd.getMargins("tb"));
14494         var hh = this.header.getHeight();
14495         var h = this.size.height-hh;
14496         cb.setHeight(h);
14497         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14498         bw.setHeight(h-cb.getPadding("tb"));
14499         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14500         bd.setWidth(bw.getWidth(true));
14501         if(this.tabs){
14502             this.tabs.syncHeight();
14503             if(Roo.isIE){
14504                 this.tabs.el.repaint();
14505             }
14506         }
14507     },
14508
14509     /**
14510      * Restores the previous state of the dialog if Roo.state is configured.
14511      * @return {Roo.BasicDialog} this
14512      */
14513     restoreState : function(){
14514         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14515         if(box && box.width){
14516             this.xy = [box.x, box.y];
14517             this.resizeTo(box.width, box.height);
14518         }
14519         return this;
14520     },
14521
14522     // private
14523     beforeShow : function(){
14524         this.expand();
14525         if(this.fixedcenter){
14526             this.xy = this.el.getCenterXY(true);
14527         }
14528         if(this.modal){
14529             Roo.get(document.body).addClass("x-body-masked");
14530             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14531             this.mask.show();
14532         }
14533         this.constrainXY();
14534     },
14535
14536     // private
14537     animShow : function(){
14538         var b = Roo.get(this.animateTarget, true).getBox();
14539         this.proxy.setSize(b.width, b.height);
14540         this.proxy.setLocation(b.x, b.y);
14541         this.proxy.show();
14542         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14543                     true, .35, this.showEl.createDelegate(this));
14544     },
14545
14546     /**
14547      * Shows the dialog.
14548      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14549      * @return {Roo.BasicDialog} this
14550      */
14551     show : function(animateTarget){
14552         if (this.fireEvent("beforeshow", this) === false){
14553             return;
14554         }
14555         if(this.syncHeightBeforeShow){
14556             this.syncBodyHeight();
14557         }else if(this.firstShow){
14558             this.firstShow = false;
14559             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14560         }
14561         this.animateTarget = animateTarget || this.animateTarget;
14562         if(!this.el.isVisible()){
14563             this.beforeShow();
14564             if(this.animateTarget){
14565                 this.animShow();
14566             }else{
14567                 this.showEl();
14568             }
14569         }
14570         return this;
14571     },
14572
14573     // private
14574     showEl : function(){
14575         this.proxy.hide();
14576         this.el.setXY(this.xy);
14577         this.el.show();
14578         this.adjustAssets(true);
14579         this.toFront();
14580         this.focus();
14581         // IE peekaboo bug - fix found by Dave Fenwick
14582         if(Roo.isIE){
14583             this.el.repaint();
14584         }
14585         this.fireEvent("show", this);
14586     },
14587
14588     /**
14589      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14590      * dialog itself will receive focus.
14591      */
14592     focus : function(){
14593         if(this.defaultButton){
14594             this.defaultButton.focus();
14595         }else{
14596             this.focusEl.focus();
14597         }
14598     },
14599
14600     // private
14601     constrainXY : function(){
14602         if(this.constraintoviewport !== false){
14603             if(!this.viewSize){
14604                 if(this.container){
14605                     var s = this.container.getSize();
14606                     this.viewSize = [s.width, s.height];
14607                 }else{
14608                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14609                 }
14610             }
14611             var s = Roo.get(this.container||document).getScroll();
14612
14613             var x = this.xy[0], y = this.xy[1];
14614             var w = this.size.width, h = this.size.height;
14615             var vw = this.viewSize[0], vh = this.viewSize[1];
14616             // only move it if it needs it
14617             var moved = false;
14618             // first validate right/bottom
14619             if(x + w > vw+s.left){
14620                 x = vw - w;
14621                 moved = true;
14622             }
14623             if(y + h > vh+s.top){
14624                 y = vh - h;
14625                 moved = true;
14626             }
14627             // then make sure top/left isn't negative
14628             if(x < s.left){
14629                 x = s.left;
14630                 moved = true;
14631             }
14632             if(y < s.top){
14633                 y = s.top;
14634                 moved = true;
14635             }
14636             if(moved){
14637                 // cache xy
14638                 this.xy = [x, y];
14639                 if(this.isVisible()){
14640                     this.el.setLocation(x, y);
14641                     this.adjustAssets();
14642                 }
14643             }
14644         }
14645     },
14646
14647     // private
14648     onDrag : function(){
14649         if(!this.proxyDrag){
14650             this.xy = this.el.getXY();
14651             this.adjustAssets();
14652         }
14653     },
14654
14655     // private
14656     adjustAssets : function(doShow){
14657         var x = this.xy[0], y = this.xy[1];
14658         var w = this.size.width, h = this.size.height;
14659         if(doShow === true){
14660             if(this.shadow){
14661                 this.shadow.show(this.el);
14662             }
14663             if(this.shim){
14664                 this.shim.show();
14665             }
14666         }
14667         if(this.shadow && this.shadow.isVisible()){
14668             this.shadow.show(this.el);
14669         }
14670         if(this.shim && this.shim.isVisible()){
14671             this.shim.setBounds(x, y, w, h);
14672         }
14673     },
14674
14675     // private
14676     adjustViewport : function(w, h){
14677         if(!w || !h){
14678             w = Roo.lib.Dom.getViewWidth();
14679             h = Roo.lib.Dom.getViewHeight();
14680         }
14681         // cache the size
14682         this.viewSize = [w, h];
14683         if(this.modal && this.mask.isVisible()){
14684             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14685             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14686         }
14687         if(this.isVisible()){
14688             this.constrainXY();
14689         }
14690     },
14691
14692     /**
14693      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14694      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14695      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14696      */
14697     destroy : function(removeEl){
14698         if(this.isVisible()){
14699             this.animateTarget = null;
14700             this.hide();
14701         }
14702         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14703         if(this.tabs){
14704             this.tabs.destroy(removeEl);
14705         }
14706         Roo.destroy(
14707              this.shim,
14708              this.proxy,
14709              this.resizer,
14710              this.close,
14711              this.mask
14712         );
14713         if(this.dd){
14714             this.dd.unreg();
14715         }
14716         if(this.buttons){
14717            for(var i = 0, len = this.buttons.length; i < len; i++){
14718                this.buttons[i].destroy();
14719            }
14720         }
14721         this.el.removeAllListeners();
14722         if(removeEl === true){
14723             this.el.update("");
14724             this.el.remove();
14725         }
14726         Roo.DialogManager.unregister(this);
14727     },
14728
14729     // private
14730     startMove : function(){
14731         if(this.proxyDrag){
14732             this.proxy.show();
14733         }
14734         if(this.constraintoviewport !== false){
14735             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14736         }
14737     },
14738
14739     // private
14740     endMove : function(){
14741         if(!this.proxyDrag){
14742             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14743         }else{
14744             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14745             this.proxy.hide();
14746         }
14747         this.refreshSize();
14748         this.adjustAssets();
14749         this.focus();
14750         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14751     },
14752
14753     /**
14754      * Brings this dialog to the front of any other visible dialogs
14755      * @return {Roo.BasicDialog} this
14756      */
14757     toFront : function(){
14758         Roo.DialogManager.bringToFront(this);
14759         return this;
14760     },
14761
14762     /**
14763      * Sends this dialog to the back (under) of any other visible dialogs
14764      * @return {Roo.BasicDialog} this
14765      */
14766     toBack : function(){
14767         Roo.DialogManager.sendToBack(this);
14768         return this;
14769     },
14770
14771     /**
14772      * Centers this dialog in the viewport
14773      * @return {Roo.BasicDialog} this
14774      */
14775     center : function(){
14776         var xy = this.el.getCenterXY(true);
14777         this.moveTo(xy[0], xy[1]);
14778         return this;
14779     },
14780
14781     /**
14782      * Moves the dialog's top-left corner to the specified point
14783      * @param {Number} x
14784      * @param {Number} y
14785      * @return {Roo.BasicDialog} this
14786      */
14787     moveTo : function(x, y){
14788         this.xy = [x,y];
14789         if(this.isVisible()){
14790             this.el.setXY(this.xy);
14791             this.adjustAssets();
14792         }
14793         return this;
14794     },
14795
14796     /**
14797      * Aligns the dialog to the specified element
14798      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14799      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14800      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14801      * @return {Roo.BasicDialog} this
14802      */
14803     alignTo : function(element, position, offsets){
14804         this.xy = this.el.getAlignToXY(element, position, offsets);
14805         if(this.isVisible()){
14806             this.el.setXY(this.xy);
14807             this.adjustAssets();
14808         }
14809         return this;
14810     },
14811
14812     /**
14813      * Anchors an element to another element and realigns it when the window is resized.
14814      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14815      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14816      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14817      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14818      * is a number, it is used as the buffer delay (defaults to 50ms).
14819      * @return {Roo.BasicDialog} this
14820      */
14821     anchorTo : function(el, alignment, offsets, monitorScroll){
14822         var action = function(){
14823             this.alignTo(el, alignment, offsets);
14824         };
14825         Roo.EventManager.onWindowResize(action, this);
14826         var tm = typeof monitorScroll;
14827         if(tm != 'undefined'){
14828             Roo.EventManager.on(window, 'scroll', action, this,
14829                 {buffer: tm == 'number' ? monitorScroll : 50});
14830         }
14831         action.call(this);
14832         return this;
14833     },
14834
14835     /**
14836      * Returns true if the dialog is visible
14837      * @return {Boolean}
14838      */
14839     isVisible : function(){
14840         return this.el.isVisible();
14841     },
14842
14843     // private
14844     animHide : function(callback){
14845         var b = Roo.get(this.animateTarget).getBox();
14846         this.proxy.show();
14847         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14848         this.el.hide();
14849         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14850                     this.hideEl.createDelegate(this, [callback]));
14851     },
14852
14853     /**
14854      * Hides the dialog.
14855      * @param {Function} callback (optional) Function to call when the dialog is hidden
14856      * @return {Roo.BasicDialog} this
14857      */
14858     hide : function(callback){
14859         if (this.fireEvent("beforehide", this) === false){
14860             return;
14861         }
14862         if(this.shadow){
14863             this.shadow.hide();
14864         }
14865         if(this.shim) {
14866           this.shim.hide();
14867         }
14868         if(this.animateTarget){
14869            this.animHide(callback);
14870         }else{
14871             this.el.hide();
14872             this.hideEl(callback);
14873         }
14874         return this;
14875     },
14876
14877     // private
14878     hideEl : function(callback){
14879         this.proxy.hide();
14880         if(this.modal){
14881             this.mask.hide();
14882             Roo.get(document.body).removeClass("x-body-masked");
14883         }
14884         this.fireEvent("hide", this);
14885         if(typeof callback == "function"){
14886             callback();
14887         }
14888     },
14889
14890     // private
14891     hideAction : function(){
14892         this.setLeft("-10000px");
14893         this.setTop("-10000px");
14894         this.setStyle("visibility", "hidden");
14895     },
14896
14897     // private
14898     refreshSize : function(){
14899         this.size = this.el.getSize();
14900         this.xy = this.el.getXY();
14901         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14902     },
14903
14904     // private
14905     // z-index is managed by the DialogManager and may be overwritten at any time
14906     setZIndex : function(index){
14907         if(this.modal){
14908             this.mask.setStyle("z-index", index);
14909         }
14910         if(this.shim){
14911             this.shim.setStyle("z-index", ++index);
14912         }
14913         if(this.shadow){
14914             this.shadow.setZIndex(++index);
14915         }
14916         this.el.setStyle("z-index", ++index);
14917         if(this.proxy){
14918             this.proxy.setStyle("z-index", ++index);
14919         }
14920         if(this.resizer){
14921             this.resizer.proxy.setStyle("z-index", ++index);
14922         }
14923
14924         this.lastZIndex = index;
14925     },
14926
14927     /**
14928      * Returns the element for this dialog
14929      * @return {Roo.Element} The underlying dialog Element
14930      */
14931     getEl : function(){
14932         return this.el;
14933     }
14934 });
14935
14936 /**
14937  * @class Roo.DialogManager
14938  * Provides global access to BasicDialogs that have been created and
14939  * support for z-indexing (layering) multiple open dialogs.
14940  */
14941 Roo.DialogManager = function(){
14942     var list = {};
14943     var accessList = [];
14944     var front = null;
14945
14946     // private
14947     var sortDialogs = function(d1, d2){
14948         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14949     };
14950
14951     // private
14952     var orderDialogs = function(){
14953         accessList.sort(sortDialogs);
14954         var seed = Roo.DialogManager.zseed;
14955         for(var i = 0, len = accessList.length; i < len; i++){
14956             var dlg = accessList[i];
14957             if(dlg){
14958                 dlg.setZIndex(seed + (i*10));
14959             }
14960         }
14961     };
14962
14963     return {
14964         /**
14965          * The starting z-index for BasicDialogs (defaults to 9000)
14966          * @type Number The z-index value
14967          */
14968         zseed : 9000,
14969
14970         // private
14971         register : function(dlg){
14972             list[dlg.id] = dlg;
14973             accessList.push(dlg);
14974         },
14975
14976         // private
14977         unregister : function(dlg){
14978             delete list[dlg.id];
14979             var i=0;
14980             var len=0;
14981             if(!accessList.indexOf){
14982                 for(  i = 0, len = accessList.length; i < len; i++){
14983                     if(accessList[i] == dlg){
14984                         accessList.splice(i, 1);
14985                         return;
14986                     }
14987                 }
14988             }else{
14989                  i = accessList.indexOf(dlg);
14990                 if(i != -1){
14991                     accessList.splice(i, 1);
14992                 }
14993             }
14994         },
14995
14996         /**
14997          * Gets a registered dialog by id
14998          * @param {String/Object} id The id of the dialog or a dialog
14999          * @return {Roo.BasicDialog} this
15000          */
15001         get : function(id){
15002             return typeof id == "object" ? id : list[id];
15003         },
15004
15005         /**
15006          * Brings the specified dialog to the front
15007          * @param {String/Object} dlg The id of the dialog or a dialog
15008          * @return {Roo.BasicDialog} this
15009          */
15010         bringToFront : function(dlg){
15011             dlg = this.get(dlg);
15012             if(dlg != front){
15013                 front = dlg;
15014                 dlg._lastAccess = new Date().getTime();
15015                 orderDialogs();
15016             }
15017             return dlg;
15018         },
15019
15020         /**
15021          * Sends the specified dialog to the back
15022          * @param {String/Object} dlg The id of the dialog or a dialog
15023          * @return {Roo.BasicDialog} this
15024          */
15025         sendToBack : function(dlg){
15026             dlg = this.get(dlg);
15027             dlg._lastAccess = -(new Date().getTime());
15028             orderDialogs();
15029             return dlg;
15030         },
15031
15032         /**
15033          * Hides all dialogs
15034          */
15035         hideAll : function(){
15036             for(var id in list){
15037                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15038                     list[id].hide();
15039                 }
15040             }
15041         }
15042     };
15043 }();
15044
15045 /**
15046  * @class Roo.LayoutDialog
15047  * @extends Roo.BasicDialog
15048  * Dialog which provides adjustments for working with a layout in a Dialog.
15049  * Add your necessary layout config options to the dialog's config.<br>
15050  * Example usage (including a nested layout):
15051  * <pre><code>
15052 if(!dialog){
15053     dialog = new Roo.LayoutDialog("download-dlg", {
15054         modal: true,
15055         width:600,
15056         height:450,
15057         shadow:true,
15058         minWidth:500,
15059         minHeight:350,
15060         autoTabs:true,
15061         proxyDrag:true,
15062         // layout config merges with the dialog config
15063         center:{
15064             tabPosition: "top",
15065             alwaysShowTabs: true
15066         }
15067     });
15068     dialog.addKeyListener(27, dialog.hide, dialog);
15069     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15070     dialog.addButton("Build It!", this.getDownload, this);
15071
15072     // we can even add nested layouts
15073     var innerLayout = new Roo.BorderLayout("dl-inner", {
15074         east: {
15075             initialSize: 200,
15076             autoScroll:true,
15077             split:true
15078         },
15079         center: {
15080             autoScroll:true
15081         }
15082     });
15083     innerLayout.beginUpdate();
15084     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15085     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15086     innerLayout.endUpdate(true);
15087
15088     var layout = dialog.getLayout();
15089     layout.beginUpdate();
15090     layout.add("center", new Roo.ContentPanel("standard-panel",
15091                         {title: "Download the Source", fitToFrame:true}));
15092     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15093                {title: "Build your own roo.js"}));
15094     layout.getRegion("center").showPanel(sp);
15095     layout.endUpdate();
15096 }
15097 </code></pre>
15098     * @constructor
15099     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15100     * @param {Object} config configuration options
15101   */
15102 Roo.LayoutDialog = function(el, cfg){
15103     
15104     var config=  cfg;
15105     if (typeof(cfg) == 'undefined') {
15106         config = Roo.apply({}, el);
15107         el = Roo.get( document.documentElement || document.body).createChild();
15108         //config.autoCreate = true;
15109     }
15110     
15111     
15112     config.autoTabs = false;
15113     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15114     this.body.setStyle({overflow:"hidden", position:"relative"});
15115     this.layout = new Roo.BorderLayout(this.body.dom, config);
15116     this.layout.monitorWindowResize = false;
15117     this.el.addClass("x-dlg-auto-layout");
15118     // fix case when center region overwrites center function
15119     this.center = Roo.BasicDialog.prototype.center;
15120     this.on("show", this.layout.layout, this.layout, true);
15121     if (config.items) {
15122         var xitems = config.items;
15123         delete config.items;
15124         Roo.each(xitems, this.addxtype, this);
15125     }
15126     
15127     
15128 };
15129 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15130     /**
15131      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15132      * @deprecated
15133      */
15134     endUpdate : function(){
15135         this.layout.endUpdate();
15136     },
15137
15138     /**
15139      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15140      *  @deprecated
15141      */
15142     beginUpdate : function(){
15143         this.layout.beginUpdate();
15144     },
15145
15146     /**
15147      * Get the BorderLayout for this dialog
15148      * @return {Roo.BorderLayout}
15149      */
15150     getLayout : function(){
15151         return this.layout;
15152     },
15153
15154     showEl : function(){
15155         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15156         if(Roo.isIE7){
15157             this.layout.layout();
15158         }
15159     },
15160
15161     // private
15162     // Use the syncHeightBeforeShow config option to control this automatically
15163     syncBodyHeight : function(){
15164         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15165         if(this.layout){this.layout.layout();}
15166     },
15167     
15168       /**
15169      * Add an xtype element (actually adds to the layout.)
15170      * @return {Object} xdata xtype object data.
15171      */
15172     
15173     addxtype : function(c) {
15174         return this.layout.addxtype(c);
15175     }
15176 });/*
15177  * Based on:
15178  * Ext JS Library 1.1.1
15179  * Copyright(c) 2006-2007, Ext JS, LLC.
15180  *
15181  * Originally Released Under LGPL - original licence link has changed is not relivant.
15182  *
15183  * Fork - LGPL
15184  * <script type="text/javascript">
15185  */
15186  
15187 /**
15188  * @class Roo.MessageBox
15189  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15190  * Example usage:
15191  *<pre><code>
15192 // Basic alert:
15193 Roo.Msg.alert('Status', 'Changes saved successfully.');
15194
15195 // Prompt for user data:
15196 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15197     if (btn == 'ok'){
15198         // process text value...
15199     }
15200 });
15201
15202 // Show a dialog using config options:
15203 Roo.Msg.show({
15204    title:'Save Changes?',
15205    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15206    buttons: Roo.Msg.YESNOCANCEL,
15207    fn: processResult,
15208    animEl: 'elId'
15209 });
15210 </code></pre>
15211  * @singleton
15212  */
15213 Roo.MessageBox = function(){
15214     var dlg, opt, mask, waitTimer;
15215     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15216     var buttons, activeTextEl, bwidth;
15217
15218     // private
15219     var handleButton = function(button){
15220         dlg.hide();
15221         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15222     };
15223
15224     // private
15225     var handleHide = function(){
15226         if(opt && opt.cls){
15227             dlg.el.removeClass(opt.cls);
15228         }
15229         if(waitTimer){
15230             Roo.TaskMgr.stop(waitTimer);
15231             waitTimer = null;
15232         }
15233     };
15234
15235     // private
15236     var updateButtons = function(b){
15237         var width = 0;
15238         if(!b){
15239             buttons["ok"].hide();
15240             buttons["cancel"].hide();
15241             buttons["yes"].hide();
15242             buttons["no"].hide();
15243             dlg.footer.dom.style.display = 'none';
15244             return width;
15245         }
15246         dlg.footer.dom.style.display = '';
15247         for(var k in buttons){
15248             if(typeof buttons[k] != "function"){
15249                 if(b[k]){
15250                     buttons[k].show();
15251                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15252                     width += buttons[k].el.getWidth()+15;
15253                 }else{
15254                     buttons[k].hide();
15255                 }
15256             }
15257         }
15258         return width;
15259     };
15260
15261     // private
15262     var handleEsc = function(d, k, e){
15263         if(opt && opt.closable !== false){
15264             dlg.hide();
15265         }
15266         if(e){
15267             e.stopEvent();
15268         }
15269     };
15270
15271     return {
15272         /**
15273          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15274          * @return {Roo.BasicDialog} The BasicDialog element
15275          */
15276         getDialog : function(){
15277            if(!dlg){
15278                 dlg = new Roo.BasicDialog("x-msg-box", {
15279                     autoCreate : true,
15280                     shadow: true,
15281                     draggable: true,
15282                     resizable:false,
15283                     constraintoviewport:false,
15284                     fixedcenter:true,
15285                     collapsible : false,
15286                     shim:true,
15287                     modal: true,
15288                     width:400, height:100,
15289                     buttonAlign:"center",
15290                     closeClick : function(){
15291                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15292                             handleButton("no");
15293                         }else{
15294                             handleButton("cancel");
15295                         }
15296                     }
15297                 });
15298                 dlg.on("hide", handleHide);
15299                 mask = dlg.mask;
15300                 dlg.addKeyListener(27, handleEsc);
15301                 buttons = {};
15302                 var bt = this.buttonText;
15303                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15304                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15305                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15306                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15307                 bodyEl = dlg.body.createChild({
15308
15309                     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>'
15310                 });
15311                 msgEl = bodyEl.dom.firstChild;
15312                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15313                 textboxEl.enableDisplayMode();
15314                 textboxEl.addKeyListener([10,13], function(){
15315                     if(dlg.isVisible() && opt && opt.buttons){
15316                         if(opt.buttons.ok){
15317                             handleButton("ok");
15318                         }else if(opt.buttons.yes){
15319                             handleButton("yes");
15320                         }
15321                     }
15322                 });
15323                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15324                 textareaEl.enableDisplayMode();
15325                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15326                 progressEl.enableDisplayMode();
15327                 var pf = progressEl.dom.firstChild;
15328                 if (pf) {
15329                     pp = Roo.get(pf.firstChild);
15330                     pp.setHeight(pf.offsetHeight);
15331                 }
15332                 
15333             }
15334             return dlg;
15335         },
15336
15337         /**
15338          * Updates the message box body text
15339          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15340          * the XHTML-compliant non-breaking space character '&amp;#160;')
15341          * @return {Roo.MessageBox} This message box
15342          */
15343         updateText : function(text){
15344             if(!dlg.isVisible() && !opt.width){
15345                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15346             }
15347             msgEl.innerHTML = text || '&#160;';
15348             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15349                         Math.max(opt.minWidth || this.minWidth, bwidth));
15350             if(opt.prompt){
15351                 activeTextEl.setWidth(w);
15352             }
15353             if(dlg.isVisible()){
15354                 dlg.fixedcenter = false;
15355             }
15356             dlg.setContentSize(w, bodyEl.getHeight());
15357             if(dlg.isVisible()){
15358                 dlg.fixedcenter = true;
15359             }
15360             return this;
15361         },
15362
15363         /**
15364          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15365          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15366          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15367          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15368          * @return {Roo.MessageBox} This message box
15369          */
15370         updateProgress : function(value, text){
15371             if(text){
15372                 this.updateText(text);
15373             }
15374             if (pp) { // weird bug on my firefox - for some reason this is not defined
15375                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15376             }
15377             return this;
15378         },        
15379
15380         /**
15381          * Returns true if the message box is currently displayed
15382          * @return {Boolean} True if the message box is visible, else false
15383          */
15384         isVisible : function(){
15385             return dlg && dlg.isVisible();  
15386         },
15387
15388         /**
15389          * Hides the message box if it is displayed
15390          */
15391         hide : function(){
15392             if(this.isVisible()){
15393                 dlg.hide();
15394             }  
15395         },
15396
15397         /**
15398          * Displays a new message box, or reinitializes an existing message box, based on the config options
15399          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15400          * The following config object properties are supported:
15401          * <pre>
15402 Property    Type             Description
15403 ----------  ---------------  ------------------------------------------------------------------------------------
15404 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15405                                    closes (defaults to undefined)
15406 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15407                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15408 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15409                                    progress and wait dialogs will ignore this property and always hide the
15410                                    close button as they can only be closed programmatically.
15411 cls               String           A custom CSS class to apply to the message box element
15412 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15413                                    displayed (defaults to 75)
15414 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15415                                    function will be btn (the name of the button that was clicked, if applicable,
15416                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15417                                    Progress and wait dialogs will ignore this option since they do not respond to
15418                                    user actions and can only be closed programmatically, so any required function
15419                                    should be called by the same code after it closes the dialog.
15420 icon              String           A CSS class that provides a background image to be used as an icon for
15421                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15422 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15423 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15424 modal             Boolean          False to allow user interaction with the page while the message box is
15425                                    displayed (defaults to true)
15426 msg               String           A string that will replace the existing message box body text (defaults
15427                                    to the XHTML-compliant non-breaking space character '&#160;')
15428 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15429 progress          Boolean          True to display a progress bar (defaults to false)
15430 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15431 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15432 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15433 title             String           The title text
15434 value             String           The string value to set into the active textbox element if displayed
15435 wait              Boolean          True to display a progress bar (defaults to false)
15436 width             Number           The width of the dialog in pixels
15437 </pre>
15438          *
15439          * Example usage:
15440          * <pre><code>
15441 Roo.Msg.show({
15442    title: 'Address',
15443    msg: 'Please enter your address:',
15444    width: 300,
15445    buttons: Roo.MessageBox.OKCANCEL,
15446    multiline: true,
15447    fn: saveAddress,
15448    animEl: 'addAddressBtn'
15449 });
15450 </code></pre>
15451          * @param {Object} config Configuration options
15452          * @return {Roo.MessageBox} This message box
15453          */
15454         show : function(options){
15455             if(this.isVisible()){
15456                 this.hide();
15457             }
15458             var d = this.getDialog();
15459             opt = options;
15460             d.setTitle(opt.title || "&#160;");
15461             d.close.setDisplayed(opt.closable !== false);
15462             activeTextEl = textboxEl;
15463             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15464             if(opt.prompt){
15465                 if(opt.multiline){
15466                     textboxEl.hide();
15467                     textareaEl.show();
15468                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15469                         opt.multiline : this.defaultTextHeight);
15470                     activeTextEl = textareaEl;
15471                 }else{
15472                     textboxEl.show();
15473                     textareaEl.hide();
15474                 }
15475             }else{
15476                 textboxEl.hide();
15477                 textareaEl.hide();
15478             }
15479             progressEl.setDisplayed(opt.progress === true);
15480             this.updateProgress(0);
15481             activeTextEl.dom.value = opt.value || "";
15482             if(opt.prompt){
15483                 dlg.setDefaultButton(activeTextEl);
15484             }else{
15485                 var bs = opt.buttons;
15486                 var db = null;
15487                 if(bs && bs.ok){
15488                     db = buttons["ok"];
15489                 }else if(bs && bs.yes){
15490                     db = buttons["yes"];
15491                 }
15492                 dlg.setDefaultButton(db);
15493             }
15494             bwidth = updateButtons(opt.buttons);
15495             this.updateText(opt.msg);
15496             if(opt.cls){
15497                 d.el.addClass(opt.cls);
15498             }
15499             d.proxyDrag = opt.proxyDrag === true;
15500             d.modal = opt.modal !== false;
15501             d.mask = opt.modal !== false ? mask : false;
15502             if(!d.isVisible()){
15503                 // force it to the end of the z-index stack so it gets a cursor in FF
15504                 document.body.appendChild(dlg.el.dom);
15505                 d.animateTarget = null;
15506                 d.show(options.animEl);
15507             }
15508             return this;
15509         },
15510
15511         /**
15512          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15513          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15514          * and closing the message box when the process is complete.
15515          * @param {String} title The title bar text
15516          * @param {String} msg The message box body text
15517          * @return {Roo.MessageBox} This message box
15518          */
15519         progress : function(title, msg){
15520             this.show({
15521                 title : title,
15522                 msg : msg,
15523                 buttons: false,
15524                 progress:true,
15525                 closable:false,
15526                 minWidth: this.minProgressWidth,
15527                 modal : true
15528             });
15529             return this;
15530         },
15531
15532         /**
15533          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15534          * If a callback function is passed it will be called after the user clicks the button, and the
15535          * id of the button that was clicked will be passed as the only parameter to the callback
15536          * (could also be the top-right close button).
15537          * @param {String} title The title bar text
15538          * @param {String} msg The message box body text
15539          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15540          * @param {Object} scope (optional) The scope of the callback function
15541          * @return {Roo.MessageBox} This message box
15542          */
15543         alert : function(title, msg, fn, scope){
15544             this.show({
15545                 title : title,
15546                 msg : msg,
15547                 buttons: this.OK,
15548                 fn: fn,
15549                 scope : scope,
15550                 modal : true
15551             });
15552             return this;
15553         },
15554
15555         /**
15556          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15557          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15558          * You are responsible for closing the message box when the process is complete.
15559          * @param {String} msg The message box body text
15560          * @param {String} title (optional) The title bar text
15561          * @return {Roo.MessageBox} This message box
15562          */
15563         wait : function(msg, title){
15564             this.show({
15565                 title : title,
15566                 msg : msg,
15567                 buttons: false,
15568                 closable:false,
15569                 progress:true,
15570                 modal:true,
15571                 width:300,
15572                 wait:true
15573             });
15574             waitTimer = Roo.TaskMgr.start({
15575                 run: function(i){
15576                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15577                 },
15578                 interval: 1000
15579             });
15580             return this;
15581         },
15582
15583         /**
15584          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15585          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15586          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15587          * @param {String} title The title bar text
15588          * @param {String} msg The message box body text
15589          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15590          * @param {Object} scope (optional) The scope of the callback function
15591          * @return {Roo.MessageBox} This message box
15592          */
15593         confirm : function(title, msg, fn, scope){
15594             this.show({
15595                 title : title,
15596                 msg : msg,
15597                 buttons: this.YESNO,
15598                 fn: fn,
15599                 scope : scope,
15600                 modal : true
15601             });
15602             return this;
15603         },
15604
15605         /**
15606          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15607          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15608          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15609          * (could also be the top-right close button) and the text that was entered will be passed as the two
15610          * parameters to the callback.
15611          * @param {String} title The title bar text
15612          * @param {String} msg The message box body text
15613          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15614          * @param {Object} scope (optional) The scope of the callback function
15615          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15616          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15617          * @return {Roo.MessageBox} This message box
15618          */
15619         prompt : function(title, msg, fn, scope, multiline){
15620             this.show({
15621                 title : title,
15622                 msg : msg,
15623                 buttons: this.OKCANCEL,
15624                 fn: fn,
15625                 minWidth:250,
15626                 scope : scope,
15627                 prompt:true,
15628                 multiline: multiline,
15629                 modal : true
15630             });
15631             return this;
15632         },
15633
15634         /**
15635          * Button config that displays a single OK button
15636          * @type Object
15637          */
15638         OK : {ok:true},
15639         /**
15640          * Button config that displays Yes and No buttons
15641          * @type Object
15642          */
15643         YESNO : {yes:true, no:true},
15644         /**
15645          * Button config that displays OK and Cancel buttons
15646          * @type Object
15647          */
15648         OKCANCEL : {ok:true, cancel:true},
15649         /**
15650          * Button config that displays Yes, No and Cancel buttons
15651          * @type Object
15652          */
15653         YESNOCANCEL : {yes:true, no:true, cancel:true},
15654
15655         /**
15656          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15657          * @type Number
15658          */
15659         defaultTextHeight : 75,
15660         /**
15661          * The maximum width in pixels of the message box (defaults to 600)
15662          * @type Number
15663          */
15664         maxWidth : 600,
15665         /**
15666          * The minimum width in pixels of the message box (defaults to 100)
15667          * @type Number
15668          */
15669         minWidth : 100,
15670         /**
15671          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15672          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15673          * @type Number
15674          */
15675         minProgressWidth : 250,
15676         /**
15677          * An object containing the default button text strings that can be overriden for localized language support.
15678          * Supported properties are: ok, cancel, yes and no.
15679          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15680          * @type Object
15681          */
15682         buttonText : {
15683             ok : "OK",
15684             cancel : "Cancel",
15685             yes : "Yes",
15686             no : "No"
15687         }
15688     };
15689 }();
15690
15691 /**
15692  * Shorthand for {@link Roo.MessageBox}
15693  */
15694 Roo.Msg = Roo.MessageBox;/*
15695  * Based on:
15696  * Ext JS Library 1.1.1
15697  * Copyright(c) 2006-2007, Ext JS, LLC.
15698  *
15699  * Originally Released Under LGPL - original licence link has changed is not relivant.
15700  *
15701  * Fork - LGPL
15702  * <script type="text/javascript">
15703  */
15704 /**
15705  * @class Roo.QuickTips
15706  * Provides attractive and customizable tooltips for any element.
15707  * @singleton
15708  */
15709 Roo.QuickTips = function(){
15710     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15711     var ce, bd, xy, dd;
15712     var visible = false, disabled = true, inited = false;
15713     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15714     
15715     var onOver = function(e){
15716         if(disabled){
15717             return;
15718         }
15719         var t = e.getTarget();
15720         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15721             return;
15722         }
15723         if(ce && t == ce.el){
15724             clearTimeout(hideProc);
15725             return;
15726         }
15727         if(t && tagEls[t.id]){
15728             tagEls[t.id].el = t;
15729             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15730             return;
15731         }
15732         var ttp, et = Roo.fly(t);
15733         var ns = cfg.namespace;
15734         if(tm.interceptTitles && t.title){
15735             ttp = t.title;
15736             t.qtip = ttp;
15737             t.removeAttribute("title");
15738             e.preventDefault();
15739         }else{
15740             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15741         }
15742         if(ttp){
15743             showProc = show.defer(tm.showDelay, tm, [{
15744                 el: t, 
15745                 text: ttp, 
15746                 width: et.getAttributeNS(ns, cfg.width),
15747                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15748                 title: et.getAttributeNS(ns, cfg.title),
15749                     cls: et.getAttributeNS(ns, cfg.cls)
15750             }]);
15751         }
15752     };
15753     
15754     var onOut = function(e){
15755         clearTimeout(showProc);
15756         var t = e.getTarget();
15757         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15758             hideProc = setTimeout(hide, tm.hideDelay);
15759         }
15760     };
15761     
15762     var onMove = function(e){
15763         if(disabled){
15764             return;
15765         }
15766         xy = e.getXY();
15767         xy[1] += 18;
15768         if(tm.trackMouse && ce){
15769             el.setXY(xy);
15770         }
15771     };
15772     
15773     var onDown = function(e){
15774         clearTimeout(showProc);
15775         clearTimeout(hideProc);
15776         if(!e.within(el)){
15777             if(tm.hideOnClick){
15778                 hide();
15779                 tm.disable();
15780                 tm.enable.defer(100, tm);
15781             }
15782         }
15783     };
15784     
15785     var getPad = function(){
15786         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15787     };
15788
15789     var show = function(o){
15790         if(disabled){
15791             return;
15792         }
15793         clearTimeout(dismissProc);
15794         ce = o;
15795         if(removeCls){ // in case manually hidden
15796             el.removeClass(removeCls);
15797             removeCls = null;
15798         }
15799         if(ce.cls){
15800             el.addClass(ce.cls);
15801             removeCls = ce.cls;
15802         }
15803         if(ce.title){
15804             tipTitle.update(ce.title);
15805             tipTitle.show();
15806         }else{
15807             tipTitle.update('');
15808             tipTitle.hide();
15809         }
15810         el.dom.style.width  = tm.maxWidth+'px';
15811         //tipBody.dom.style.width = '';
15812         tipBodyText.update(o.text);
15813         var p = getPad(), w = ce.width;
15814         if(!w){
15815             var td = tipBodyText.dom;
15816             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15817             if(aw > tm.maxWidth){
15818                 w = tm.maxWidth;
15819             }else if(aw < tm.minWidth){
15820                 w = tm.minWidth;
15821             }else{
15822                 w = aw;
15823             }
15824         }
15825         //tipBody.setWidth(w);
15826         el.setWidth(parseInt(w, 10) + p);
15827         if(ce.autoHide === false){
15828             close.setDisplayed(true);
15829             if(dd){
15830                 dd.unlock();
15831             }
15832         }else{
15833             close.setDisplayed(false);
15834             if(dd){
15835                 dd.lock();
15836             }
15837         }
15838         if(xy){
15839             el.avoidY = xy[1]-18;
15840             el.setXY(xy);
15841         }
15842         if(tm.animate){
15843             el.setOpacity(.1);
15844             el.setStyle("visibility", "visible");
15845             el.fadeIn({callback: afterShow});
15846         }else{
15847             afterShow();
15848         }
15849     };
15850     
15851     var afterShow = function(){
15852         if(ce){
15853             el.show();
15854             esc.enable();
15855             if(tm.autoDismiss && ce.autoHide !== false){
15856                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15857             }
15858         }
15859     };
15860     
15861     var hide = function(noanim){
15862         clearTimeout(dismissProc);
15863         clearTimeout(hideProc);
15864         ce = null;
15865         if(el.isVisible()){
15866             esc.disable();
15867             if(noanim !== true && tm.animate){
15868                 el.fadeOut({callback: afterHide});
15869             }else{
15870                 afterHide();
15871             } 
15872         }
15873     };
15874     
15875     var afterHide = function(){
15876         el.hide();
15877         if(removeCls){
15878             el.removeClass(removeCls);
15879             removeCls = null;
15880         }
15881     };
15882     
15883     return {
15884         /**
15885         * @cfg {Number} minWidth
15886         * The minimum width of the quick tip (defaults to 40)
15887         */
15888        minWidth : 40,
15889         /**
15890         * @cfg {Number} maxWidth
15891         * The maximum width of the quick tip (defaults to 300)
15892         */
15893        maxWidth : 300,
15894         /**
15895         * @cfg {Boolean} interceptTitles
15896         * True to automatically use the element's DOM title value if available (defaults to false)
15897         */
15898        interceptTitles : false,
15899         /**
15900         * @cfg {Boolean} trackMouse
15901         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15902         */
15903        trackMouse : false,
15904         /**
15905         * @cfg {Boolean} hideOnClick
15906         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15907         */
15908        hideOnClick : true,
15909         /**
15910         * @cfg {Number} showDelay
15911         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15912         */
15913        showDelay : 500,
15914         /**
15915         * @cfg {Number} hideDelay
15916         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15917         */
15918        hideDelay : 200,
15919         /**
15920         * @cfg {Boolean} autoHide
15921         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15922         * Used in conjunction with hideDelay.
15923         */
15924        autoHide : true,
15925         /**
15926         * @cfg {Boolean}
15927         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15928         * (defaults to true).  Used in conjunction with autoDismissDelay.
15929         */
15930        autoDismiss : true,
15931         /**
15932         * @cfg {Number}
15933         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15934         */
15935        autoDismissDelay : 5000,
15936        /**
15937         * @cfg {Boolean} animate
15938         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15939         */
15940        animate : false,
15941
15942        /**
15943         * @cfg {String} title
15944         * Title text to display (defaults to '').  This can be any valid HTML markup.
15945         */
15946         title: '',
15947        /**
15948         * @cfg {String} text
15949         * Body text to display (defaults to '').  This can be any valid HTML markup.
15950         */
15951         text : '',
15952        /**
15953         * @cfg {String} cls
15954         * A CSS class to apply to the base quick tip element (defaults to '').
15955         */
15956         cls : '',
15957        /**
15958         * @cfg {Number} width
15959         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15960         * minWidth or maxWidth.
15961         */
15962         width : null,
15963
15964     /**
15965      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15966      * or display QuickTips in a page.
15967      */
15968        init : function(){
15969           tm = Roo.QuickTips;
15970           cfg = tm.tagConfig;
15971           if(!inited){
15972               if(!Roo.isReady){ // allow calling of init() before onReady
15973                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15974                   return;
15975               }
15976               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15977               el.fxDefaults = {stopFx: true};
15978               // maximum custom styling
15979               //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>');
15980               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>');              
15981               tipTitle = el.child('h3');
15982               tipTitle.enableDisplayMode("block");
15983               tipBody = el.child('div.x-tip-bd');
15984               tipBodyText = el.child('div.x-tip-bd-inner');
15985               //bdLeft = el.child('div.x-tip-bd-left');
15986               //bdRight = el.child('div.x-tip-bd-right');
15987               close = el.child('div.x-tip-close');
15988               close.enableDisplayMode("block");
15989               close.on("click", hide);
15990               var d = Roo.get(document);
15991               d.on("mousedown", onDown);
15992               d.on("mouseover", onOver);
15993               d.on("mouseout", onOut);
15994               d.on("mousemove", onMove);
15995               esc = d.addKeyListener(27, hide);
15996               esc.disable();
15997               if(Roo.dd.DD){
15998                   dd = el.initDD("default", null, {
15999                       onDrag : function(){
16000                           el.sync();  
16001                       }
16002                   });
16003                   dd.setHandleElId(tipTitle.id);
16004                   dd.lock();
16005               }
16006               inited = true;
16007           }
16008           this.enable(); 
16009        },
16010
16011     /**
16012      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16013      * are supported:
16014      * <pre>
16015 Property    Type                   Description
16016 ----------  ---------------------  ------------------------------------------------------------------------
16017 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16018      * </ul>
16019      * @param {Object} config The config object
16020      */
16021        register : function(config){
16022            var cs = config instanceof Array ? config : arguments;
16023            for(var i = 0, len = cs.length; i < len; i++) {
16024                var c = cs[i];
16025                var target = c.target;
16026                if(target){
16027                    if(target instanceof Array){
16028                        for(var j = 0, jlen = target.length; j < jlen; j++){
16029                            tagEls[target[j]] = c;
16030                        }
16031                    }else{
16032                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16033                    }
16034                }
16035            }
16036        },
16037
16038     /**
16039      * Removes this quick tip from its element and destroys it.
16040      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16041      */
16042        unregister : function(el){
16043            delete tagEls[Roo.id(el)];
16044        },
16045
16046     /**
16047      * Enable this quick tip.
16048      */
16049        enable : function(){
16050            if(inited && disabled){
16051                locks.pop();
16052                if(locks.length < 1){
16053                    disabled = false;
16054                }
16055            }
16056        },
16057
16058     /**
16059      * Disable this quick tip.
16060      */
16061        disable : function(){
16062           disabled = true;
16063           clearTimeout(showProc);
16064           clearTimeout(hideProc);
16065           clearTimeout(dismissProc);
16066           if(ce){
16067               hide(true);
16068           }
16069           locks.push(1);
16070        },
16071
16072     /**
16073      * Returns true if the quick tip is enabled, else false.
16074      */
16075        isEnabled : function(){
16076             return !disabled;
16077        },
16078
16079         // private
16080        tagConfig : {
16081            namespace : "ext",
16082            attribute : "qtip",
16083            width : "width",
16084            target : "target",
16085            title : "qtitle",
16086            hide : "hide",
16087            cls : "qclass"
16088        }
16089    };
16090 }();
16091
16092 // backwards compat
16093 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16094  * Based on:
16095  * Ext JS Library 1.1.1
16096  * Copyright(c) 2006-2007, Ext JS, LLC.
16097  *
16098  * Originally Released Under LGPL - original licence link has changed is not relivant.
16099  *
16100  * Fork - LGPL
16101  * <script type="text/javascript">
16102  */
16103  
16104
16105 /**
16106  * @class Roo.tree.TreePanel
16107  * @extends Roo.data.Tree
16108
16109  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16110  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16111  * @cfg {Boolean} enableDD true to enable drag and drop
16112  * @cfg {Boolean} enableDrag true to enable just drag
16113  * @cfg {Boolean} enableDrop true to enable just drop
16114  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16115  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16116  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16117  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16118  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16119  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16120  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16121  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16122  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16123  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16124  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16125  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16126  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16127  * @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>
16128  * @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>
16129  * 
16130  * @constructor
16131  * @param {String/HTMLElement/Element} el The container element
16132  * @param {Object} config
16133  */
16134 Roo.tree.TreePanel = function(el, config){
16135     var root = false;
16136     var loader = false;
16137     if (config.root) {
16138         root = config.root;
16139         delete config.root;
16140     }
16141     if (config.loader) {
16142         loader = config.loader;
16143         delete config.loader;
16144     }
16145     
16146     Roo.apply(this, config);
16147     Roo.tree.TreePanel.superclass.constructor.call(this);
16148     this.el = Roo.get(el);
16149     this.el.addClass('x-tree');
16150     //console.log(root);
16151     if (root) {
16152         this.setRootNode( Roo.factory(root, Roo.tree));
16153     }
16154     if (loader) {
16155         this.loader = Roo.factory(loader, Roo.tree);
16156     }
16157    /**
16158     * Read-only. The id of the container element becomes this TreePanel's id.
16159     */
16160    this.id = this.el.id;
16161    this.addEvents({
16162         /**
16163         * @event beforeload
16164         * Fires before a node is loaded, return false to cancel
16165         * @param {Node} node The node being loaded
16166         */
16167         "beforeload" : true,
16168         /**
16169         * @event load
16170         * Fires when a node is loaded
16171         * @param {Node} node The node that was loaded
16172         */
16173         "load" : true,
16174         /**
16175         * @event textchange
16176         * Fires when the text for a node is changed
16177         * @param {Node} node The node
16178         * @param {String} text The new text
16179         * @param {String} oldText The old text
16180         */
16181         "textchange" : true,
16182         /**
16183         * @event beforeexpand
16184         * Fires before a node is expanded, return false to cancel.
16185         * @param {Node} node The node
16186         * @param {Boolean} deep
16187         * @param {Boolean} anim
16188         */
16189         "beforeexpand" : true,
16190         /**
16191         * @event beforecollapse
16192         * Fires before a node is collapsed, return false to cancel.
16193         * @param {Node} node The node
16194         * @param {Boolean} deep
16195         * @param {Boolean} anim
16196         */
16197         "beforecollapse" : true,
16198         /**
16199         * @event expand
16200         * Fires when a node is expanded
16201         * @param {Node} node The node
16202         */
16203         "expand" : true,
16204         /**
16205         * @event disabledchange
16206         * Fires when the disabled status of a node changes
16207         * @param {Node} node The node
16208         * @param {Boolean} disabled
16209         */
16210         "disabledchange" : true,
16211         /**
16212         * @event collapse
16213         * Fires when a node is collapsed
16214         * @param {Node} node The node
16215         */
16216         "collapse" : true,
16217         /**
16218         * @event beforeclick
16219         * Fires before click processing on a node. Return false to cancel the default action.
16220         * @param {Node} node The node
16221         * @param {Roo.EventObject} e The event object
16222         */
16223         "beforeclick":true,
16224         /**
16225         * @event checkchange
16226         * Fires when a node with a checkbox's checked property changes
16227         * @param {Node} this This node
16228         * @param {Boolean} checked
16229         */
16230         "checkchange":true,
16231         /**
16232         * @event click
16233         * Fires when a node is clicked
16234         * @param {Node} node The node
16235         * @param {Roo.EventObject} e The event object
16236         */
16237         "click":true,
16238         /**
16239         * @event dblclick
16240         * Fires when a node is double clicked
16241         * @param {Node} node The node
16242         * @param {Roo.EventObject} e The event object
16243         */
16244         "dblclick":true,
16245         /**
16246         * @event contextmenu
16247         * Fires when a node is right clicked
16248         * @param {Node} node The node
16249         * @param {Roo.EventObject} e The event object
16250         */
16251         "contextmenu":true,
16252         /**
16253         * @event beforechildrenrendered
16254         * Fires right before the child nodes for a node are rendered
16255         * @param {Node} node The node
16256         */
16257         "beforechildrenrendered":true,
16258        /**
16259              * @event startdrag
16260              * Fires when a node starts being dragged
16261              * @param {Roo.tree.TreePanel} this
16262              * @param {Roo.tree.TreeNode} node
16263              * @param {event} e The raw browser event
16264              */ 
16265             "startdrag" : true,
16266             /**
16267              * @event enddrag
16268              * Fires when a drag operation is complete
16269              * @param {Roo.tree.TreePanel} this
16270              * @param {Roo.tree.TreeNode} node
16271              * @param {event} e The raw browser event
16272              */
16273             "enddrag" : true,
16274             /**
16275              * @event dragdrop
16276              * Fires when a dragged node is dropped on a valid DD target
16277              * @param {Roo.tree.TreePanel} this
16278              * @param {Roo.tree.TreeNode} node
16279              * @param {DD} dd The dd it was dropped on
16280              * @param {event} e The raw browser event
16281              */
16282             "dragdrop" : true,
16283             /**
16284              * @event beforenodedrop
16285              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16286              * passed to handlers has the following properties:<br />
16287              * <ul style="padding:5px;padding-left:16px;">
16288              * <li>tree - The TreePanel</li>
16289              * <li>target - The node being targeted for the drop</li>
16290              * <li>data - The drag data from the drag source</li>
16291              * <li>point - The point of the drop - append, above or below</li>
16292              * <li>source - The drag source</li>
16293              * <li>rawEvent - Raw mouse event</li>
16294              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16295              * to be inserted by setting them on this object.</li>
16296              * <li>cancel - Set this to true to cancel the drop.</li>
16297              * </ul>
16298              * @param {Object} dropEvent
16299              */
16300             "beforenodedrop" : true,
16301             /**
16302              * @event nodedrop
16303              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16304              * passed to handlers has the following properties:<br />
16305              * <ul style="padding:5px;padding-left:16px;">
16306              * <li>tree - The TreePanel</li>
16307              * <li>target - The node being targeted for the drop</li>
16308              * <li>data - The drag data from the drag source</li>
16309              * <li>point - The point of the drop - append, above or below</li>
16310              * <li>source - The drag source</li>
16311              * <li>rawEvent - Raw mouse event</li>
16312              * <li>dropNode - Dropped node(s).</li>
16313              * </ul>
16314              * @param {Object} dropEvent
16315              */
16316             "nodedrop" : true,
16317              /**
16318              * @event nodedragover
16319              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16320              * passed to handlers has the following properties:<br />
16321              * <ul style="padding:5px;padding-left:16px;">
16322              * <li>tree - The TreePanel</li>
16323              * <li>target - The node being targeted for the drop</li>
16324              * <li>data - The drag data from the drag source</li>
16325              * <li>point - The point of the drop - append, above or below</li>
16326              * <li>source - The drag source</li>
16327              * <li>rawEvent - Raw mouse event</li>
16328              * <li>dropNode - Drop node(s) provided by the source.</li>
16329              * <li>cancel - Set this to true to signal drop not allowed.</li>
16330              * </ul>
16331              * @param {Object} dragOverEvent
16332              */
16333             "nodedragover" : true
16334         
16335    });
16336    if(this.singleExpand){
16337        this.on("beforeexpand", this.restrictExpand, this);
16338    }
16339 };
16340 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16341     rootVisible : true,
16342     animate: Roo.enableFx,
16343     lines : true,
16344     enableDD : false,
16345     hlDrop : Roo.enableFx,
16346   
16347     renderer: false,
16348     
16349     rendererTip: false,
16350     // private
16351     restrictExpand : function(node){
16352         var p = node.parentNode;
16353         if(p){
16354             if(p.expandedChild && p.expandedChild.parentNode == p){
16355                 p.expandedChild.collapse();
16356             }
16357             p.expandedChild = node;
16358         }
16359     },
16360
16361     // private override
16362     setRootNode : function(node){
16363         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16364         if(!this.rootVisible){
16365             node.ui = new Roo.tree.RootTreeNodeUI(node);
16366         }
16367         return node;
16368     },
16369
16370     /**
16371      * Returns the container element for this TreePanel
16372      */
16373     getEl : function(){
16374         return this.el;
16375     },
16376
16377     /**
16378      * Returns the default TreeLoader for this TreePanel
16379      */
16380     getLoader : function(){
16381         return this.loader;
16382     },
16383
16384     /**
16385      * Expand all nodes
16386      */
16387     expandAll : function(){
16388         this.root.expand(true);
16389     },
16390
16391     /**
16392      * Collapse all nodes
16393      */
16394     collapseAll : function(){
16395         this.root.collapse(true);
16396     },
16397
16398     /**
16399      * Returns the selection model used by this TreePanel
16400      */
16401     getSelectionModel : function(){
16402         if(!this.selModel){
16403             this.selModel = new Roo.tree.DefaultSelectionModel();
16404         }
16405         return this.selModel;
16406     },
16407
16408     /**
16409      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16410      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16411      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16412      * @return {Array}
16413      */
16414     getChecked : function(a, startNode){
16415         startNode = startNode || this.root;
16416         var r = [];
16417         var f = function(){
16418             if(this.attributes.checked){
16419                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16420             }
16421         }
16422         startNode.cascade(f);
16423         return r;
16424     },
16425
16426     /**
16427      * Expands a specified path in this TreePanel. 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 expand is complete. The callback will be called with
16431      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16432      */
16433     expandPath : function(path, attr, callback){
16434         attr = attr || "id";
16435         var keys = path.split(this.pathSeparator);
16436         var curNode = this.root;
16437         if(curNode.attributes[attr] != keys[1]){ // invalid root
16438             if(callback){
16439                 callback(false, null);
16440             }
16441             return;
16442         }
16443         var index = 1;
16444         var f = function(){
16445             if(++index == keys.length){
16446                 if(callback){
16447                     callback(true, curNode);
16448                 }
16449                 return;
16450             }
16451             var c = curNode.findChild(attr, keys[index]);
16452             if(!c){
16453                 if(callback){
16454                     callback(false, curNode);
16455                 }
16456                 return;
16457             }
16458             curNode = c;
16459             c.expand(false, false, f);
16460         };
16461         curNode.expand(false, false, f);
16462     },
16463
16464     /**
16465      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16466      * @param {String} path
16467      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16468      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16469      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16470      */
16471     selectPath : function(path, attr, callback){
16472         attr = attr || "id";
16473         var keys = path.split(this.pathSeparator);
16474         var v = keys.pop();
16475         if(keys.length > 0){
16476             var f = function(success, node){
16477                 if(success && node){
16478                     var n = node.findChild(attr, v);
16479                     if(n){
16480                         n.select();
16481                         if(callback){
16482                             callback(true, n);
16483                         }
16484                     }else if(callback){
16485                         callback(false, n);
16486                     }
16487                 }else{
16488                     if(callback){
16489                         callback(false, n);
16490                     }
16491                 }
16492             };
16493             this.expandPath(keys.join(this.pathSeparator), attr, f);
16494         }else{
16495             this.root.select();
16496             if(callback){
16497                 callback(true, this.root);
16498             }
16499         }
16500     },
16501
16502     getTreeEl : function(){
16503         return this.el;
16504     },
16505
16506     /**
16507      * Trigger rendering of this TreePanel
16508      */
16509     render : function(){
16510         if (this.innerCt) {
16511             return this; // stop it rendering more than once!!
16512         }
16513         
16514         this.innerCt = this.el.createChild({tag:"ul",
16515                cls:"x-tree-root-ct " +
16516                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16517
16518         if(this.containerScroll){
16519             Roo.dd.ScrollManager.register(this.el);
16520         }
16521         if((this.enableDD || this.enableDrop) && !this.dropZone){
16522            /**
16523             * The dropZone used by this tree if drop is enabled
16524             * @type Roo.tree.TreeDropZone
16525             */
16526              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16527                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16528            });
16529         }
16530         if((this.enableDD || this.enableDrag) && !this.dragZone){
16531            /**
16532             * The dragZone used by this tree if drag is enabled
16533             * @type Roo.tree.TreeDragZone
16534             */
16535             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16536                ddGroup: this.ddGroup || "TreeDD",
16537                scroll: this.ddScroll
16538            });
16539         }
16540         this.getSelectionModel().init(this);
16541         if (!this.root) {
16542             console.log("ROOT not set in tree");
16543             return;
16544         }
16545         this.root.render();
16546         if(!this.rootVisible){
16547             this.root.renderChildren();
16548         }
16549         return this;
16550     }
16551 });/*
16552  * Based on:
16553  * Ext JS Library 1.1.1
16554  * Copyright(c) 2006-2007, Ext JS, LLC.
16555  *
16556  * Originally Released Under LGPL - original licence link has changed is not relivant.
16557  *
16558  * Fork - LGPL
16559  * <script type="text/javascript">
16560  */
16561  
16562
16563 /**
16564  * @class Roo.tree.DefaultSelectionModel
16565  * @extends Roo.util.Observable
16566  * The default single selection for a TreePanel.
16567  */
16568 Roo.tree.DefaultSelectionModel = function(){
16569    this.selNode = null;
16570    
16571    this.addEvents({
16572        /**
16573         * @event selectionchange
16574         * Fires when the selected node changes
16575         * @param {DefaultSelectionModel} this
16576         * @param {TreeNode} node the new selection
16577         */
16578        "selectionchange" : true,
16579
16580        /**
16581         * @event beforeselect
16582         * Fires before the selected node changes, return false to cancel the change
16583         * @param {DefaultSelectionModel} this
16584         * @param {TreeNode} node the new selection
16585         * @param {TreeNode} node the old selection
16586         */
16587        "beforeselect" : true
16588    });
16589 };
16590
16591 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16592     init : function(tree){
16593         this.tree = tree;
16594         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16595         tree.on("click", this.onNodeClick, this);
16596     },
16597     
16598     onNodeClick : function(node, e){
16599         if (e.ctrlKey && this.selNode == node)  {
16600             this.unselect(node);
16601             return;
16602         }
16603         this.select(node);
16604     },
16605     
16606     /**
16607      * Select a node.
16608      * @param {TreeNode} node The node to select
16609      * @return {TreeNode} The selected node
16610      */
16611     select : function(node){
16612         var last = this.selNode;
16613         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16614             if(last){
16615                 last.ui.onSelectedChange(false);
16616             }
16617             this.selNode = node;
16618             node.ui.onSelectedChange(true);
16619             this.fireEvent("selectionchange", this, node, last);
16620         }
16621         return node;
16622     },
16623     
16624     /**
16625      * Deselect a node.
16626      * @param {TreeNode} node The node to unselect
16627      */
16628     unselect : function(node){
16629         if(this.selNode == node){
16630             this.clearSelections();
16631         }    
16632     },
16633     
16634     /**
16635      * Clear all selections
16636      */
16637     clearSelections : function(){
16638         var n = this.selNode;
16639         if(n){
16640             n.ui.onSelectedChange(false);
16641             this.selNode = null;
16642             this.fireEvent("selectionchange", this, null);
16643         }
16644         return n;
16645     },
16646     
16647     /**
16648      * Get the selected node
16649      * @return {TreeNode} The selected node
16650      */
16651     getSelectedNode : function(){
16652         return this.selNode;    
16653     },
16654     
16655     /**
16656      * Returns true if the node is selected
16657      * @param {TreeNode} node The node to check
16658      * @return {Boolean}
16659      */
16660     isSelected : function(node){
16661         return this.selNode == node;  
16662     },
16663
16664     /**
16665      * Selects the node above the selected node in the tree, intelligently walking the nodes
16666      * @return TreeNode The new selection
16667      */
16668     selectPrevious : function(){
16669         var s = this.selNode || this.lastSelNode;
16670         if(!s){
16671             return null;
16672         }
16673         var ps = s.previousSibling;
16674         if(ps){
16675             if(!ps.isExpanded() || ps.childNodes.length < 1){
16676                 return this.select(ps);
16677             } else{
16678                 var lc = ps.lastChild;
16679                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16680                     lc = lc.lastChild;
16681                 }
16682                 return this.select(lc);
16683             }
16684         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16685             return this.select(s.parentNode);
16686         }
16687         return null;
16688     },
16689
16690     /**
16691      * Selects the node above the selected node in the tree, intelligently walking the nodes
16692      * @return TreeNode The new selection
16693      */
16694     selectNext : function(){
16695         var s = this.selNode || this.lastSelNode;
16696         if(!s){
16697             return null;
16698         }
16699         if(s.firstChild && s.isExpanded()){
16700              return this.select(s.firstChild);
16701          }else if(s.nextSibling){
16702              return this.select(s.nextSibling);
16703          }else if(s.parentNode){
16704             var newS = null;
16705             s.parentNode.bubble(function(){
16706                 if(this.nextSibling){
16707                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16708                     return false;
16709                 }
16710             });
16711             return newS;
16712          }
16713         return null;
16714     },
16715
16716     onKeyDown : function(e){
16717         var s = this.selNode || this.lastSelNode;
16718         // undesirable, but required
16719         var sm = this;
16720         if(!s){
16721             return;
16722         }
16723         var k = e.getKey();
16724         switch(k){
16725              case e.DOWN:
16726                  e.stopEvent();
16727                  this.selectNext();
16728              break;
16729              case e.UP:
16730                  e.stopEvent();
16731                  this.selectPrevious();
16732              break;
16733              case e.RIGHT:
16734                  e.preventDefault();
16735                  if(s.hasChildNodes()){
16736                      if(!s.isExpanded()){
16737                          s.expand();
16738                      }else if(s.firstChild){
16739                          this.select(s.firstChild, e);
16740                      }
16741                  }
16742              break;
16743              case e.LEFT:
16744                  e.preventDefault();
16745                  if(s.hasChildNodes() && s.isExpanded()){
16746                      s.collapse();
16747                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16748                      this.select(s.parentNode, e);
16749                  }
16750              break;
16751         };
16752     }
16753 });
16754
16755 /**
16756  * @class Roo.tree.MultiSelectionModel
16757  * @extends Roo.util.Observable
16758  * Multi selection for a TreePanel.
16759  */
16760 Roo.tree.MultiSelectionModel = function(){
16761    this.selNodes = [];
16762    this.selMap = {};
16763    this.addEvents({
16764        /**
16765         * @event selectionchange
16766         * Fires when the selected nodes change
16767         * @param {MultiSelectionModel} this
16768         * @param {Array} nodes Array of the selected nodes
16769         */
16770        "selectionchange" : true
16771    });
16772 };
16773
16774 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16775     init : function(tree){
16776         this.tree = tree;
16777         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16778         tree.on("click", this.onNodeClick, this);
16779     },
16780     
16781     onNodeClick : function(node, e){
16782         this.select(node, e, e.ctrlKey);
16783     },
16784     
16785     /**
16786      * Select a node.
16787      * @param {TreeNode} node The node to select
16788      * @param {EventObject} e (optional) An event associated with the selection
16789      * @param {Boolean} keepExisting True to retain existing selections
16790      * @return {TreeNode} The selected node
16791      */
16792     select : function(node, e, keepExisting){
16793         if(keepExisting !== true){
16794             this.clearSelections(true);
16795         }
16796         if(this.isSelected(node)){
16797             this.lastSelNode = node;
16798             return node;
16799         }
16800         this.selNodes.push(node);
16801         this.selMap[node.id] = node;
16802         this.lastSelNode = node;
16803         node.ui.onSelectedChange(true);
16804         this.fireEvent("selectionchange", this, this.selNodes);
16805         return node;
16806     },
16807     
16808     /**
16809      * Deselect a node.
16810      * @param {TreeNode} node The node to unselect
16811      */
16812     unselect : function(node){
16813         if(this.selMap[node.id]){
16814             node.ui.onSelectedChange(false);
16815             var sn = this.selNodes;
16816             var index = -1;
16817             if(sn.indexOf){
16818                 index = sn.indexOf(node);
16819             }else{
16820                 for(var i = 0, len = sn.length; i < len; i++){
16821                     if(sn[i] == node){
16822                         index = i;
16823                         break;
16824                     }
16825                 }
16826             }
16827             if(index != -1){
16828                 this.selNodes.splice(index, 1);
16829             }
16830             delete this.selMap[node.id];
16831             this.fireEvent("selectionchange", this, this.selNodes);
16832         }
16833     },
16834     
16835     /**
16836      * Clear all selections
16837      */
16838     clearSelections : function(suppressEvent){
16839         var sn = this.selNodes;
16840         if(sn.length > 0){
16841             for(var i = 0, len = sn.length; i < len; i++){
16842                 sn[i].ui.onSelectedChange(false);
16843             }
16844             this.selNodes = [];
16845             this.selMap = {};
16846             if(suppressEvent !== true){
16847                 this.fireEvent("selectionchange", this, this.selNodes);
16848             }
16849         }
16850     },
16851     
16852     /**
16853      * Returns true if the node is selected
16854      * @param {TreeNode} node The node to check
16855      * @return {Boolean}
16856      */
16857     isSelected : function(node){
16858         return this.selMap[node.id] ? true : false;  
16859     },
16860     
16861     /**
16862      * Returns an array of the selected nodes
16863      * @return {Array}
16864      */
16865     getSelectedNodes : function(){
16866         return this.selNodes;    
16867     },
16868
16869     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16870
16871     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16872
16873     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16874 });/*
16875  * Based on:
16876  * Ext JS Library 1.1.1
16877  * Copyright(c) 2006-2007, Ext JS, LLC.
16878  *
16879  * Originally Released Under LGPL - original licence link has changed is not relivant.
16880  *
16881  * Fork - LGPL
16882  * <script type="text/javascript">
16883  */
16884  
16885 /**
16886  * @class Roo.tree.TreeNode
16887  * @extends Roo.data.Node
16888  * @cfg {String} text The text for this node
16889  * @cfg {Boolean} expanded true to start the node expanded
16890  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16891  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16892  * @cfg {Boolean} disabled true to start the node disabled
16893  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16894  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16895  * @cfg {String} cls A css class to be added to the node
16896  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16897  * @cfg {String} href URL of the link used for the node (defaults to #)
16898  * @cfg {String} hrefTarget target frame for the link
16899  * @cfg {String} qtip An Ext QuickTip for the node
16900  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16901  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16902  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16903  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16904  * (defaults to undefined with no checkbox rendered)
16905  * @constructor
16906  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16907  */
16908 Roo.tree.TreeNode = function(attributes){
16909     attributes = attributes || {};
16910     if(typeof attributes == "string"){
16911         attributes = {text: attributes};
16912     }
16913     this.childrenRendered = false;
16914     this.rendered = false;
16915     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16916     this.expanded = attributes.expanded === true;
16917     this.isTarget = attributes.isTarget !== false;
16918     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16919     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16920
16921     /**
16922      * Read-only. The text for this node. To change it use setText().
16923      * @type String
16924      */
16925     this.text = attributes.text;
16926     /**
16927      * True if this node is disabled.
16928      * @type Boolean
16929      */
16930     this.disabled = attributes.disabled === true;
16931
16932     this.addEvents({
16933         /**
16934         * @event textchange
16935         * Fires when the text for this node is changed
16936         * @param {Node} this This node
16937         * @param {String} text The new text
16938         * @param {String} oldText The old text
16939         */
16940         "textchange" : true,
16941         /**
16942         * @event beforeexpand
16943         * Fires before this node is expanded, return false to cancel.
16944         * @param {Node} this This node
16945         * @param {Boolean} deep
16946         * @param {Boolean} anim
16947         */
16948         "beforeexpand" : true,
16949         /**
16950         * @event beforecollapse
16951         * Fires before this node is collapsed, return false to cancel.
16952         * @param {Node} this This node
16953         * @param {Boolean} deep
16954         * @param {Boolean} anim
16955         */
16956         "beforecollapse" : true,
16957         /**
16958         * @event expand
16959         * Fires when this node is expanded
16960         * @param {Node} this This node
16961         */
16962         "expand" : true,
16963         /**
16964         * @event disabledchange
16965         * Fires when the disabled status of this node changes
16966         * @param {Node} this This node
16967         * @param {Boolean} disabled
16968         */
16969         "disabledchange" : true,
16970         /**
16971         * @event collapse
16972         * Fires when this node is collapsed
16973         * @param {Node} this This node
16974         */
16975         "collapse" : true,
16976         /**
16977         * @event beforeclick
16978         * Fires before click processing. Return false to cancel the default action.
16979         * @param {Node} this This node
16980         * @param {Roo.EventObject} e The event object
16981         */
16982         "beforeclick":true,
16983         /**
16984         * @event checkchange
16985         * Fires when a node with a checkbox's checked property changes
16986         * @param {Node} this This node
16987         * @param {Boolean} checked
16988         */
16989         "checkchange":true,
16990         /**
16991         * @event click
16992         * Fires when this node is clicked
16993         * @param {Node} this This node
16994         * @param {Roo.EventObject} e The event object
16995         */
16996         "click":true,
16997         /**
16998         * @event dblclick
16999         * Fires when this node is double clicked
17000         * @param {Node} this This node
17001         * @param {Roo.EventObject} e The event object
17002         */
17003         "dblclick":true,
17004         /**
17005         * @event contextmenu
17006         * Fires when this node is right clicked
17007         * @param {Node} this This node
17008         * @param {Roo.EventObject} e The event object
17009         */
17010         "contextmenu":true,
17011         /**
17012         * @event beforechildrenrendered
17013         * Fires right before the child nodes for this node are rendered
17014         * @param {Node} this This node
17015         */
17016         "beforechildrenrendered":true
17017     });
17018
17019     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17020
17021     /**
17022      * Read-only. The UI for this node
17023      * @type TreeNodeUI
17024      */
17025     this.ui = new uiClass(this);
17026 };
17027 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17028     preventHScroll: true,
17029     /**
17030      * Returns true if this node is expanded
17031      * @return {Boolean}
17032      */
17033     isExpanded : function(){
17034         return this.expanded;
17035     },
17036
17037     /**
17038      * Returns the UI object for this node
17039      * @return {TreeNodeUI}
17040      */
17041     getUI : function(){
17042         return this.ui;
17043     },
17044
17045     // private override
17046     setFirstChild : function(node){
17047         var of = this.firstChild;
17048         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17049         if(this.childrenRendered && of && node != of){
17050             of.renderIndent(true, true);
17051         }
17052         if(this.rendered){
17053             this.renderIndent(true, true);
17054         }
17055     },
17056
17057     // private override
17058     setLastChild : function(node){
17059         var ol = this.lastChild;
17060         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17061         if(this.childrenRendered && ol && node != ol){
17062             ol.renderIndent(true, true);
17063         }
17064         if(this.rendered){
17065             this.renderIndent(true, true);
17066         }
17067     },
17068
17069     // these methods are overridden to provide lazy rendering support
17070     // private override
17071     appendChild : function(){
17072         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17073         if(node && this.childrenRendered){
17074             node.render();
17075         }
17076         this.ui.updateExpandIcon();
17077         return node;
17078     },
17079
17080     // private override
17081     removeChild : function(node){
17082         this.ownerTree.getSelectionModel().unselect(node);
17083         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17084         // if it's been rendered remove dom node
17085         if(this.childrenRendered){
17086             node.ui.remove();
17087         }
17088         if(this.childNodes.length < 1){
17089             this.collapse(false, false);
17090         }else{
17091             this.ui.updateExpandIcon();
17092         }
17093         if(!this.firstChild) {
17094             this.childrenRendered = false;
17095         }
17096         return node;
17097     },
17098
17099     // private override
17100     insertBefore : function(node, refNode){
17101         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17102         if(newNode && refNode && this.childrenRendered){
17103             node.render();
17104         }
17105         this.ui.updateExpandIcon();
17106         return newNode;
17107     },
17108
17109     /**
17110      * Sets the text for this node
17111      * @param {String} text
17112      */
17113     setText : function(text){
17114         var oldText = this.text;
17115         this.text = text;
17116         this.attributes.text = text;
17117         if(this.rendered){ // event without subscribing
17118             this.ui.onTextChange(this, text, oldText);
17119         }
17120         this.fireEvent("textchange", this, text, oldText);
17121     },
17122
17123     /**
17124      * Triggers selection of this node
17125      */
17126     select : function(){
17127         this.getOwnerTree().getSelectionModel().select(this);
17128     },
17129
17130     /**
17131      * Triggers deselection of this node
17132      */
17133     unselect : function(){
17134         this.getOwnerTree().getSelectionModel().unselect(this);
17135     },
17136
17137     /**
17138      * Returns true if this node is selected
17139      * @return {Boolean}
17140      */
17141     isSelected : function(){
17142         return this.getOwnerTree().getSelectionModel().isSelected(this);
17143     },
17144
17145     /**
17146      * Expand this node.
17147      * @param {Boolean} deep (optional) True to expand all children as well
17148      * @param {Boolean} anim (optional) false to cancel the default animation
17149      * @param {Function} callback (optional) A callback to be called when
17150      * expanding this node completes (does not wait for deep expand to complete).
17151      * Called with 1 parameter, this node.
17152      */
17153     expand : function(deep, anim, callback){
17154         if(!this.expanded){
17155             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17156                 return;
17157             }
17158             if(!this.childrenRendered){
17159                 this.renderChildren();
17160             }
17161             this.expanded = true;
17162             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17163                 this.ui.animExpand(function(){
17164                     this.fireEvent("expand", this);
17165                     if(typeof callback == "function"){
17166                         callback(this);
17167                     }
17168                     if(deep === true){
17169                         this.expandChildNodes(true);
17170                     }
17171                 }.createDelegate(this));
17172                 return;
17173             }else{
17174                 this.ui.expand();
17175                 this.fireEvent("expand", this);
17176                 if(typeof callback == "function"){
17177                     callback(this);
17178                 }
17179             }
17180         }else{
17181            if(typeof callback == "function"){
17182                callback(this);
17183            }
17184         }
17185         if(deep === true){
17186             this.expandChildNodes(true);
17187         }
17188     },
17189
17190     isHiddenRoot : function(){
17191         return this.isRoot && !this.getOwnerTree().rootVisible;
17192     },
17193
17194     /**
17195      * Collapse this node.
17196      * @param {Boolean} deep (optional) True to collapse all children as well
17197      * @param {Boolean} anim (optional) false to cancel the default animation
17198      */
17199     collapse : function(deep, anim){
17200         if(this.expanded && !this.isHiddenRoot()){
17201             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17202                 return;
17203             }
17204             this.expanded = false;
17205             if((this.getOwnerTree().animate && anim !== false) || anim){
17206                 this.ui.animCollapse(function(){
17207                     this.fireEvent("collapse", this);
17208                     if(deep === true){
17209                         this.collapseChildNodes(true);
17210                     }
17211                 }.createDelegate(this));
17212                 return;
17213             }else{
17214                 this.ui.collapse();
17215                 this.fireEvent("collapse", this);
17216             }
17217         }
17218         if(deep === true){
17219             var cs = this.childNodes;
17220             for(var i = 0, len = cs.length; i < len; i++) {
17221                 cs[i].collapse(true, false);
17222             }
17223         }
17224     },
17225
17226     // private
17227     delayedExpand : function(delay){
17228         if(!this.expandProcId){
17229             this.expandProcId = this.expand.defer(delay, this);
17230         }
17231     },
17232
17233     // private
17234     cancelExpand : function(){
17235         if(this.expandProcId){
17236             clearTimeout(this.expandProcId);
17237         }
17238         this.expandProcId = false;
17239     },
17240
17241     /**
17242      * Toggles expanded/collapsed state of the node
17243      */
17244     toggle : function(){
17245         if(this.expanded){
17246             this.collapse();
17247         }else{
17248             this.expand();
17249         }
17250     },
17251
17252     /**
17253      * Ensures all parent nodes are expanded
17254      */
17255     ensureVisible : function(callback){
17256         var tree = this.getOwnerTree();
17257         tree.expandPath(this.parentNode.getPath(), false, function(){
17258             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17259             Roo.callback(callback);
17260         }.createDelegate(this));
17261     },
17262
17263     /**
17264      * Expand all child nodes
17265      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17266      */
17267     expandChildNodes : function(deep){
17268         var cs = this.childNodes;
17269         for(var i = 0, len = cs.length; i < len; i++) {
17270                 cs[i].expand(deep);
17271         }
17272     },
17273
17274     /**
17275      * Collapse all child nodes
17276      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17277      */
17278     collapseChildNodes : function(deep){
17279         var cs = this.childNodes;
17280         for(var i = 0, len = cs.length; i < len; i++) {
17281                 cs[i].collapse(deep);
17282         }
17283     },
17284
17285     /**
17286      * Disables this node
17287      */
17288     disable : function(){
17289         this.disabled = true;
17290         this.unselect();
17291         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17292             this.ui.onDisableChange(this, true);
17293         }
17294         this.fireEvent("disabledchange", this, true);
17295     },
17296
17297     /**
17298      * Enables this node
17299      */
17300     enable : function(){
17301         this.disabled = false;
17302         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17303             this.ui.onDisableChange(this, false);
17304         }
17305         this.fireEvent("disabledchange", this, false);
17306     },
17307
17308     // private
17309     renderChildren : function(suppressEvent){
17310         if(suppressEvent !== false){
17311             this.fireEvent("beforechildrenrendered", this);
17312         }
17313         var cs = this.childNodes;
17314         for(var i = 0, len = cs.length; i < len; i++){
17315             cs[i].render(true);
17316         }
17317         this.childrenRendered = true;
17318     },
17319
17320     // private
17321     sort : function(fn, scope){
17322         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17323         if(this.childrenRendered){
17324             var cs = this.childNodes;
17325             for(var i = 0, len = cs.length; i < len; i++){
17326                 cs[i].render(true);
17327             }
17328         }
17329     },
17330
17331     // private
17332     render : function(bulkRender){
17333         this.ui.render(bulkRender);
17334         if(!this.rendered){
17335             this.rendered = true;
17336             if(this.expanded){
17337                 this.expanded = false;
17338                 this.expand(false, false);
17339             }
17340         }
17341     },
17342
17343     // private
17344     renderIndent : function(deep, refresh){
17345         if(refresh){
17346             this.ui.childIndent = null;
17347         }
17348         this.ui.renderIndent();
17349         if(deep === true && this.childrenRendered){
17350             var cs = this.childNodes;
17351             for(var i = 0, len = cs.length; i < len; i++){
17352                 cs[i].renderIndent(true, refresh);
17353             }
17354         }
17355     }
17356 });/*
17357  * Based on:
17358  * Ext JS Library 1.1.1
17359  * Copyright(c) 2006-2007, Ext JS, LLC.
17360  *
17361  * Originally Released Under LGPL - original licence link has changed is not relivant.
17362  *
17363  * Fork - LGPL
17364  * <script type="text/javascript">
17365  */
17366  
17367 /**
17368  * @class Roo.tree.AsyncTreeNode
17369  * @extends Roo.tree.TreeNode
17370  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17371  * @constructor
17372  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17373  */
17374  Roo.tree.AsyncTreeNode = function(config){
17375     this.loaded = false;
17376     this.loading = false;
17377     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17378     /**
17379     * @event beforeload
17380     * Fires before this node is loaded, return false to cancel
17381     * @param {Node} this This node
17382     */
17383     this.addEvents({'beforeload':true, 'load': true});
17384     /**
17385     * @event load
17386     * Fires when this node is loaded
17387     * @param {Node} this This node
17388     */
17389     /**
17390      * The loader used by this node (defaults to using the tree's defined loader)
17391      * @type TreeLoader
17392      * @property loader
17393      */
17394 };
17395 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17396     expand : function(deep, anim, callback){
17397         if(this.loading){ // if an async load is already running, waiting til it's done
17398             var timer;
17399             var f = function(){
17400                 if(!this.loading){ // done loading
17401                     clearInterval(timer);
17402                     this.expand(deep, anim, callback);
17403                 }
17404             }.createDelegate(this);
17405             timer = setInterval(f, 200);
17406             return;
17407         }
17408         if(!this.loaded){
17409             if(this.fireEvent("beforeload", this) === false){
17410                 return;
17411             }
17412             this.loading = true;
17413             this.ui.beforeLoad(this);
17414             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17415             if(loader){
17416                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17417                 return;
17418             }
17419         }
17420         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17421     },
17422     
17423     /**
17424      * Returns true if this node is currently loading
17425      * @return {Boolean}
17426      */
17427     isLoading : function(){
17428         return this.loading;  
17429     },
17430     
17431     loadComplete : function(deep, anim, callback){
17432         this.loading = false;
17433         this.loaded = true;
17434         this.ui.afterLoad(this);
17435         this.fireEvent("load", this);
17436         this.expand(deep, anim, callback);
17437     },
17438     
17439     /**
17440      * Returns true if this node has been loaded
17441      * @return {Boolean}
17442      */
17443     isLoaded : function(){
17444         return this.loaded;
17445     },
17446     
17447     hasChildNodes : function(){
17448         if(!this.isLeaf() && !this.loaded){
17449             return true;
17450         }else{
17451             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17452         }
17453     },
17454
17455     /**
17456      * Trigger a reload for this node
17457      * @param {Function} callback
17458      */
17459     reload : function(callback){
17460         this.collapse(false, false);
17461         while(this.firstChild){
17462             this.removeChild(this.firstChild);
17463         }
17464         this.childrenRendered = false;
17465         this.loaded = false;
17466         if(this.isHiddenRoot()){
17467             this.expanded = false;
17468         }
17469         this.expand(false, false, callback);
17470     }
17471 });/*
17472  * Based on:
17473  * Ext JS Library 1.1.1
17474  * Copyright(c) 2006-2007, Ext JS, LLC.
17475  *
17476  * Originally Released Under LGPL - original licence link has changed is not relivant.
17477  *
17478  * Fork - LGPL
17479  * <script type="text/javascript">
17480  */
17481  
17482 /**
17483  * @class Roo.tree.TreeNodeUI
17484  * @constructor
17485  * @param {Object} node The node to render
17486  * The TreeNode UI implementation is separate from the
17487  * tree implementation. Unless you are customizing the tree UI,
17488  * you should never have to use this directly.
17489  */
17490 Roo.tree.TreeNodeUI = function(node){
17491     this.node = node;
17492     this.rendered = false;
17493     this.animating = false;
17494     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17495 };
17496
17497 Roo.tree.TreeNodeUI.prototype = {
17498     removeChild : function(node){
17499         if(this.rendered){
17500             this.ctNode.removeChild(node.ui.getEl());
17501         }
17502     },
17503
17504     beforeLoad : function(){
17505          this.addClass("x-tree-node-loading");
17506     },
17507
17508     afterLoad : function(){
17509          this.removeClass("x-tree-node-loading");
17510     },
17511
17512     onTextChange : function(node, text, oldText){
17513         if(this.rendered){
17514             this.textNode.innerHTML = text;
17515         }
17516     },
17517
17518     onDisableChange : function(node, state){
17519         this.disabled = state;
17520         if(state){
17521             this.addClass("x-tree-node-disabled");
17522         }else{
17523             this.removeClass("x-tree-node-disabled");
17524         }
17525     },
17526
17527     onSelectedChange : function(state){
17528         if(state){
17529             this.focus();
17530             this.addClass("x-tree-selected");
17531         }else{
17532             //this.blur();
17533             this.removeClass("x-tree-selected");
17534         }
17535     },
17536
17537     onMove : function(tree, node, oldParent, newParent, index, refNode){
17538         this.childIndent = null;
17539         if(this.rendered){
17540             var targetNode = newParent.ui.getContainer();
17541             if(!targetNode){//target not rendered
17542                 this.holder = document.createElement("div");
17543                 this.holder.appendChild(this.wrap);
17544                 return;
17545             }
17546             var insertBefore = refNode ? refNode.ui.getEl() : null;
17547             if(insertBefore){
17548                 targetNode.insertBefore(this.wrap, insertBefore);
17549             }else{
17550                 targetNode.appendChild(this.wrap);
17551             }
17552             this.node.renderIndent(true);
17553         }
17554     },
17555
17556     addClass : function(cls){
17557         if(this.elNode){
17558             Roo.fly(this.elNode).addClass(cls);
17559         }
17560     },
17561
17562     removeClass : function(cls){
17563         if(this.elNode){
17564             Roo.fly(this.elNode).removeClass(cls);
17565         }
17566     },
17567
17568     remove : function(){
17569         if(this.rendered){
17570             this.holder = document.createElement("div");
17571             this.holder.appendChild(this.wrap);
17572         }
17573     },
17574
17575     fireEvent : function(){
17576         return this.node.fireEvent.apply(this.node, arguments);
17577     },
17578
17579     initEvents : function(){
17580         this.node.on("move", this.onMove, this);
17581         var E = Roo.EventManager;
17582         var a = this.anchor;
17583
17584         var el = Roo.fly(a, '_treeui');
17585
17586         if(Roo.isOpera){ // opera render bug ignores the CSS
17587             el.setStyle("text-decoration", "none");
17588         }
17589
17590         el.on("click", this.onClick, this);
17591         el.on("dblclick", this.onDblClick, this);
17592
17593         if(this.checkbox){
17594             Roo.EventManager.on(this.checkbox,
17595                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17596         }
17597
17598         el.on("contextmenu", this.onContextMenu, this);
17599
17600         var icon = Roo.fly(this.iconNode);
17601         icon.on("click", this.onClick, this);
17602         icon.on("dblclick", this.onDblClick, this);
17603         icon.on("contextmenu", this.onContextMenu, this);
17604         E.on(this.ecNode, "click", this.ecClick, this, true);
17605
17606         if(this.node.disabled){
17607             this.addClass("x-tree-node-disabled");
17608         }
17609         if(this.node.hidden){
17610             this.addClass("x-tree-node-disabled");
17611         }
17612         var ot = this.node.getOwnerTree();
17613         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17614         if(dd && (!this.node.isRoot || ot.rootVisible)){
17615             Roo.dd.Registry.register(this.elNode, {
17616                 node: this.node,
17617                 handles: this.getDDHandles(),
17618                 isHandle: false
17619             });
17620         }
17621     },
17622
17623     getDDHandles : function(){
17624         return [this.iconNode, this.textNode];
17625     },
17626
17627     hide : function(){
17628         if(this.rendered){
17629             this.wrap.style.display = "none";
17630         }
17631     },
17632
17633     show : function(){
17634         if(this.rendered){
17635             this.wrap.style.display = "";
17636         }
17637     },
17638
17639     onContextMenu : function(e){
17640         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17641             e.preventDefault();
17642             this.focus();
17643             this.fireEvent("contextmenu", this.node, e);
17644         }
17645     },
17646
17647     onClick : function(e){
17648         if(this.dropping){
17649             e.stopEvent();
17650             return;
17651         }
17652         if(this.fireEvent("beforeclick", this.node, e) !== false){
17653             if(!this.disabled && this.node.attributes.href){
17654                 this.fireEvent("click", this.node, e);
17655                 return;
17656             }
17657             e.preventDefault();
17658             if(this.disabled){
17659                 return;
17660             }
17661
17662             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17663                 this.node.toggle();
17664             }
17665
17666             this.fireEvent("click", this.node, e);
17667         }else{
17668             e.stopEvent();
17669         }
17670     },
17671
17672     onDblClick : function(e){
17673         e.preventDefault();
17674         if(this.disabled){
17675             return;
17676         }
17677         if(this.checkbox){
17678             this.toggleCheck();
17679         }
17680         if(!this.animating && this.node.hasChildNodes()){
17681             this.node.toggle();
17682         }
17683         this.fireEvent("dblclick", this.node, e);
17684     },
17685
17686     onCheckChange : function(){
17687         var checked = this.checkbox.checked;
17688         this.node.attributes.checked = checked;
17689         this.fireEvent('checkchange', this.node, checked);
17690     },
17691
17692     ecClick : function(e){
17693         if(!this.animating && this.node.hasChildNodes()){
17694             this.node.toggle();
17695         }
17696     },
17697
17698     startDrop : function(){
17699         this.dropping = true;
17700     },
17701
17702     // delayed drop so the click event doesn't get fired on a drop
17703     endDrop : function(){
17704        setTimeout(function(){
17705            this.dropping = false;
17706        }.createDelegate(this), 50);
17707     },
17708
17709     expand : function(){
17710         this.updateExpandIcon();
17711         this.ctNode.style.display = "";
17712     },
17713
17714     focus : function(){
17715         if(!this.node.preventHScroll){
17716             try{this.anchor.focus();
17717             }catch(e){}
17718         }else if(!Roo.isIE){
17719             try{
17720                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17721                 var l = noscroll.scrollLeft;
17722                 this.anchor.focus();
17723                 noscroll.scrollLeft = l;
17724             }catch(e){}
17725         }
17726     },
17727
17728     toggleCheck : function(value){
17729         var cb = this.checkbox;
17730         if(cb){
17731             cb.checked = (value === undefined ? !cb.checked : value);
17732         }
17733     },
17734
17735     blur : function(){
17736         try{
17737             this.anchor.blur();
17738         }catch(e){}
17739     },
17740
17741     animExpand : function(callback){
17742         var ct = Roo.get(this.ctNode);
17743         ct.stopFx();
17744         if(!this.node.hasChildNodes()){
17745             this.updateExpandIcon();
17746             this.ctNode.style.display = "";
17747             Roo.callback(callback);
17748             return;
17749         }
17750         this.animating = true;
17751         this.updateExpandIcon();
17752
17753         ct.slideIn('t', {
17754            callback : function(){
17755                this.animating = false;
17756                Roo.callback(callback);
17757             },
17758             scope: this,
17759             duration: this.node.ownerTree.duration || .25
17760         });
17761     },
17762
17763     highlight : function(){
17764         var tree = this.node.getOwnerTree();
17765         Roo.fly(this.wrap).highlight(
17766             tree.hlColor || "C3DAF9",
17767             {endColor: tree.hlBaseColor}
17768         );
17769     },
17770
17771     collapse : function(){
17772         this.updateExpandIcon();
17773         this.ctNode.style.display = "none";
17774     },
17775
17776     animCollapse : function(callback){
17777         var ct = Roo.get(this.ctNode);
17778         ct.enableDisplayMode('block');
17779         ct.stopFx();
17780
17781         this.animating = true;
17782         this.updateExpandIcon();
17783
17784         ct.slideOut('t', {
17785             callback : function(){
17786                this.animating = false;
17787                Roo.callback(callback);
17788             },
17789             scope: this,
17790             duration: this.node.ownerTree.duration || .25
17791         });
17792     },
17793
17794     getContainer : function(){
17795         return this.ctNode;
17796     },
17797
17798     getEl : function(){
17799         return this.wrap;
17800     },
17801
17802     appendDDGhost : function(ghostNode){
17803         ghostNode.appendChild(this.elNode.cloneNode(true));
17804     },
17805
17806     getDDRepairXY : function(){
17807         return Roo.lib.Dom.getXY(this.iconNode);
17808     },
17809
17810     onRender : function(){
17811         this.render();
17812     },
17813
17814     render : function(bulkRender){
17815         var n = this.node, a = n.attributes;
17816         var targetNode = n.parentNode ?
17817               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17818
17819         if(!this.rendered){
17820             this.rendered = true;
17821
17822             this.renderElements(n, a, targetNode, bulkRender);
17823
17824             if(a.qtip){
17825                if(this.textNode.setAttributeNS){
17826                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17827                    if(a.qtipTitle){
17828                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17829                    }
17830                }else{
17831                    this.textNode.setAttribute("ext:qtip", a.qtip);
17832                    if(a.qtipTitle){
17833                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17834                    }
17835                }
17836             }else if(a.qtipCfg){
17837                 a.qtipCfg.target = Roo.id(this.textNode);
17838                 Roo.QuickTips.register(a.qtipCfg);
17839             }
17840             this.initEvents();
17841             if(!this.node.expanded){
17842                 this.updateExpandIcon();
17843             }
17844         }else{
17845             if(bulkRender === true) {
17846                 targetNode.appendChild(this.wrap);
17847             }
17848         }
17849     },
17850
17851     renderElements : function(n, a, targetNode, bulkRender){
17852         // add some indent caching, this helps performance when rendering a large tree
17853         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17854         var t = n.getOwnerTree();
17855         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17856         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17857         var cb = typeof a.checked == 'boolean';
17858         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17859         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17860             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17861             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17862             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17863             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17864             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17865              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17866                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17867             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17868             "</li>"];
17869
17870         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17871             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17872                                 n.nextSibling.ui.getEl(), buf.join(""));
17873         }else{
17874             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17875         }
17876
17877         this.elNode = this.wrap.childNodes[0];
17878         this.ctNode = this.wrap.childNodes[1];
17879         var cs = this.elNode.childNodes;
17880         this.indentNode = cs[0];
17881         this.ecNode = cs[1];
17882         this.iconNode = cs[2];
17883         var index = 3;
17884         if(cb){
17885             this.checkbox = cs[3];
17886             index++;
17887         }
17888         this.anchor = cs[index];
17889         this.textNode = cs[index].firstChild;
17890     },
17891
17892     getAnchor : function(){
17893         return this.anchor;
17894     },
17895
17896     getTextEl : function(){
17897         return this.textNode;
17898     },
17899
17900     getIconEl : function(){
17901         return this.iconNode;
17902     },
17903
17904     isChecked : function(){
17905         return this.checkbox ? this.checkbox.checked : false;
17906     },
17907
17908     updateExpandIcon : function(){
17909         if(this.rendered){
17910             var n = this.node, c1, c2;
17911             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17912             var hasChild = n.hasChildNodes();
17913             if(hasChild){
17914                 if(n.expanded){
17915                     cls += "-minus";
17916                     c1 = "x-tree-node-collapsed";
17917                     c2 = "x-tree-node-expanded";
17918                 }else{
17919                     cls += "-plus";
17920                     c1 = "x-tree-node-expanded";
17921                     c2 = "x-tree-node-collapsed";
17922                 }
17923                 if(this.wasLeaf){
17924                     this.removeClass("x-tree-node-leaf");
17925                     this.wasLeaf = false;
17926                 }
17927                 if(this.c1 != c1 || this.c2 != c2){
17928                     Roo.fly(this.elNode).replaceClass(c1, c2);
17929                     this.c1 = c1; this.c2 = c2;
17930                 }
17931             }else{
17932                 if(!this.wasLeaf){
17933                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17934                     delete this.c1;
17935                     delete this.c2;
17936                     this.wasLeaf = true;
17937                 }
17938             }
17939             var ecc = "x-tree-ec-icon "+cls;
17940             if(this.ecc != ecc){
17941                 this.ecNode.className = ecc;
17942                 this.ecc = ecc;
17943             }
17944         }
17945     },
17946
17947     getChildIndent : function(){
17948         if(!this.childIndent){
17949             var buf = [];
17950             var p = this.node;
17951             while(p){
17952                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17953                     if(!p.isLast()) {
17954                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17955                     } else {
17956                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17957                     }
17958                 }
17959                 p = p.parentNode;
17960             }
17961             this.childIndent = buf.join("");
17962         }
17963         return this.childIndent;
17964     },
17965
17966     renderIndent : function(){
17967         if(this.rendered){
17968             var indent = "";
17969             var p = this.node.parentNode;
17970             if(p){
17971                 indent = p.ui.getChildIndent();
17972             }
17973             if(this.indentMarkup != indent){ // don't rerender if not required
17974                 this.indentNode.innerHTML = indent;
17975                 this.indentMarkup = indent;
17976             }
17977             this.updateExpandIcon();
17978         }
17979     }
17980 };
17981
17982 Roo.tree.RootTreeNodeUI = function(){
17983     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17984 };
17985 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17986     render : function(){
17987         if(!this.rendered){
17988             var targetNode = this.node.ownerTree.innerCt.dom;
17989             this.node.expanded = true;
17990             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17991             this.wrap = this.ctNode = targetNode.firstChild;
17992         }
17993     },
17994     collapse : function(){
17995     },
17996     expand : function(){
17997     }
17998 });/*
17999  * Based on:
18000  * Ext JS Library 1.1.1
18001  * Copyright(c) 2006-2007, Ext JS, LLC.
18002  *
18003  * Originally Released Under LGPL - original licence link has changed is not relivant.
18004  *
18005  * Fork - LGPL
18006  * <script type="text/javascript">
18007  */
18008 /**
18009  * @class Roo.tree.TreeLoader
18010  * @extends Roo.util.Observable
18011  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18012  * nodes from a specified URL. The response must be a javascript Array definition
18013  * who's elements are node definition objects. eg:
18014  * <pre><code>
18015    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18016     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18017 </code></pre>
18018  * <br><br>
18019  * A server request is sent, and child nodes are loaded only when a node is expanded.
18020  * The loading node's id is passed to the server under the parameter name "node" to
18021  * enable the server to produce the correct child nodes.
18022  * <br><br>
18023  * To pass extra parameters, an event handler may be attached to the "beforeload"
18024  * event, and the parameters specified in the TreeLoader's baseParams property:
18025  * <pre><code>
18026     myTreeLoader.on("beforeload", function(treeLoader, node) {
18027         this.baseParams.category = node.attributes.category;
18028     }, this);
18029 </code></pre><
18030  * This would pass an HTTP parameter called "category" to the server containing
18031  * the value of the Node's "category" attribute.
18032  * @constructor
18033  * Creates a new Treeloader.
18034  * @param {Object} config A config object containing config properties.
18035  */
18036 Roo.tree.TreeLoader = function(config){
18037     this.baseParams = {};
18038     this.requestMethod = "POST";
18039     Roo.apply(this, config);
18040
18041     this.addEvents({
18042     
18043         /**
18044          * @event beforeload
18045          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18046          * @param {Object} This TreeLoader object.
18047          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18048          * @param {Object} callback The callback function specified in the {@link #load} call.
18049          */
18050         beforeload : true,
18051         /**
18052          * @event load
18053          * Fires when the node has been successfuly loaded.
18054          * @param {Object} This TreeLoader object.
18055          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18056          * @param {Object} response The response object containing the data from the server.
18057          */
18058         load : true,
18059         /**
18060          * @event loadexception
18061          * Fires if the network request failed.
18062          * @param {Object} This TreeLoader object.
18063          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18064          * @param {Object} response The response object containing the data from the server.
18065          */
18066         loadexception : true,
18067         /**
18068          * @event create
18069          * Fires before a node is created, enabling you to return custom Node types 
18070          * @param {Object} This TreeLoader object.
18071          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18072          */
18073         create : true
18074     });
18075
18076     Roo.tree.TreeLoader.superclass.constructor.call(this);
18077 };
18078
18079 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18080     /**
18081     * @cfg {String} dataUrl The URL from which to request a Json string which
18082     * specifies an array of node definition object representing the child nodes
18083     * to be loaded.
18084     */
18085     /**
18086     * @cfg {Object} baseParams (optional) An object containing properties which
18087     * specify HTTP parameters to be passed to each request for child nodes.
18088     */
18089     /**
18090     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18091     * created by this loader. If the attributes sent by the server have an attribute in this object,
18092     * they take priority.
18093     */
18094     /**
18095     * @cfg {Object} uiProviders (optional) An object containing properties which
18096     * 
18097     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18098     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18099     * <i>uiProvider</i> attribute of a returned child node is a string rather
18100     * than a reference to a TreeNodeUI implementation, this that string value
18101     * is used as a property name in the uiProviders object. You can define the provider named
18102     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18103     */
18104     uiProviders : {},
18105
18106     /**
18107     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18108     * child nodes before loading.
18109     */
18110     clearOnLoad : true,
18111
18112     /**
18113     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18114     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18115     * Grid query { data : [ .....] }
18116     */
18117     
18118     root : false,
18119      /**
18120     * @cfg {String} queryParam (optional) 
18121     * Name of the query as it will be passed on the querystring (defaults to 'node')
18122     * eg. the request will be ?node=[id]
18123     */
18124     
18125     
18126     queryParam: false,
18127     
18128     /**
18129      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18130      * This is called automatically when a node is expanded, but may be used to reload
18131      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18132      * @param {Roo.tree.TreeNode} node
18133      * @param {Function} callback
18134      */
18135     load : function(node, callback){
18136         if(this.clearOnLoad){
18137             while(node.firstChild){
18138                 node.removeChild(node.firstChild);
18139             }
18140         }
18141         if(node.attributes.children){ // preloaded json children
18142             var cs = node.attributes.children;
18143             for(var i = 0, len = cs.length; i < len; i++){
18144                 node.appendChild(this.createNode(cs[i]));
18145             }
18146             if(typeof callback == "function"){
18147                 callback();
18148             }
18149         }else if(this.dataUrl){
18150             this.requestData(node, callback);
18151         }
18152     },
18153
18154     getParams: function(node){
18155         var buf = [], bp = this.baseParams;
18156         for(var key in bp){
18157             if(typeof bp[key] != "function"){
18158                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18159             }
18160         }
18161         var n = this.queryParam === false ? 'node' : this.queryParam;
18162         buf.push(n + "=", encodeURIComponent(node.id));
18163         return buf.join("");
18164     },
18165
18166     requestData : function(node, callback){
18167         if(this.fireEvent("beforeload", this, node, callback) !== false){
18168             this.transId = Roo.Ajax.request({
18169                 method:this.requestMethod,
18170                 url: this.dataUrl||this.url,
18171                 success: this.handleResponse,
18172                 failure: this.handleFailure,
18173                 scope: this,
18174                 argument: {callback: callback, node: node},
18175                 params: this.getParams(node)
18176             });
18177         }else{
18178             // if the load is cancelled, make sure we notify
18179             // the node that we are done
18180             if(typeof callback == "function"){
18181                 callback();
18182             }
18183         }
18184     },
18185
18186     isLoading : function(){
18187         return this.transId ? true : false;
18188     },
18189
18190     abort : function(){
18191         if(this.isLoading()){
18192             Roo.Ajax.abort(this.transId);
18193         }
18194     },
18195
18196     // private
18197     createNode : function(attr){
18198         // apply baseAttrs, nice idea Corey!
18199         if(this.baseAttrs){
18200             Roo.applyIf(attr, this.baseAttrs);
18201         }
18202         if(this.applyLoader !== false){
18203             attr.loader = this;
18204         }
18205         // uiProvider = depreciated..
18206         
18207         if(typeof(attr.uiProvider) == 'string'){
18208            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18209                 /**  eval:var:attr */ eval(attr.uiProvider);
18210         }
18211         if(typeof(this.uiProviders['default']) != 'undefined') {
18212             attr.uiProvider = this.uiProviders['default'];
18213         }
18214         
18215         this.fireEvent('create', this, attr);
18216         
18217         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18218         return(attr.leaf ?
18219                         new Roo.tree.TreeNode(attr) :
18220                         new Roo.tree.AsyncTreeNode(attr));
18221     },
18222
18223     processResponse : function(response, node, callback){
18224         var json = response.responseText;
18225         try {
18226             
18227             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18228             if (this.root !== false) {
18229                 o = o[this.root];
18230             }
18231             
18232             for(var i = 0, len = o.length; i < len; i++){
18233                 var n = this.createNode(o[i]);
18234                 if(n){
18235                     node.appendChild(n);
18236                 }
18237             }
18238             if(typeof callback == "function"){
18239                 callback(this, node);
18240             }
18241         }catch(e){
18242             this.handleFailure(response);
18243         }
18244     },
18245
18246     handleResponse : function(response){
18247         this.transId = false;
18248         var a = response.argument;
18249         this.processResponse(response, a.node, a.callback);
18250         this.fireEvent("load", this, a.node, response);
18251     },
18252
18253     handleFailure : function(response){
18254         this.transId = false;
18255         var a = response.argument;
18256         this.fireEvent("loadexception", this, a.node, response);
18257         if(typeof a.callback == "function"){
18258             a.callback(this, a.node);
18259         }
18260     }
18261 });/*
18262  * Based on:
18263  * Ext JS Library 1.1.1
18264  * Copyright(c) 2006-2007, Ext JS, LLC.
18265  *
18266  * Originally Released Under LGPL - original licence link has changed is not relivant.
18267  *
18268  * Fork - LGPL
18269  * <script type="text/javascript">
18270  */
18271
18272 /**
18273 * @class Roo.tree.TreeFilter
18274 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18275 * @param {TreePanel} tree
18276 * @param {Object} config (optional)
18277  */
18278 Roo.tree.TreeFilter = function(tree, config){
18279     this.tree = tree;
18280     this.filtered = {};
18281     Roo.apply(this, config);
18282 };
18283
18284 Roo.tree.TreeFilter.prototype = {
18285     clearBlank:false,
18286     reverse:false,
18287     autoClear:false,
18288     remove:false,
18289
18290      /**
18291      * Filter the data by a specific attribute.
18292      * @param {String/RegExp} value Either string that the attribute value
18293      * should start with or a RegExp to test against the attribute
18294      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18295      * @param {TreeNode} startNode (optional) The node to start the filter at.
18296      */
18297     filter : function(value, attr, startNode){
18298         attr = attr || "text";
18299         var f;
18300         if(typeof value == "string"){
18301             var vlen = value.length;
18302             // auto clear empty filter
18303             if(vlen == 0 && this.clearBlank){
18304                 this.clear();
18305                 return;
18306             }
18307             value = value.toLowerCase();
18308             f = function(n){
18309                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18310             };
18311         }else if(value.exec){ // regex?
18312             f = function(n){
18313                 return value.test(n.attributes[attr]);
18314             };
18315         }else{
18316             throw 'Illegal filter type, must be string or regex';
18317         }
18318         this.filterBy(f, null, startNode);
18319         },
18320
18321     /**
18322      * Filter by a function. The passed function will be called with each
18323      * node in the tree (or from the startNode). If the function returns true, the node is kept
18324      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18325      * @param {Function} fn The filter function
18326      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18327      */
18328     filterBy : function(fn, scope, startNode){
18329         startNode = startNode || this.tree.root;
18330         if(this.autoClear){
18331             this.clear();
18332         }
18333         var af = this.filtered, rv = this.reverse;
18334         var f = function(n){
18335             if(n == startNode){
18336                 return true;
18337             }
18338             if(af[n.id]){
18339                 return false;
18340             }
18341             var m = fn.call(scope || n, n);
18342             if(!m || rv){
18343                 af[n.id] = n;
18344                 n.ui.hide();
18345                 return false;
18346             }
18347             return true;
18348         };
18349         startNode.cascade(f);
18350         if(this.remove){
18351            for(var id in af){
18352                if(typeof id != "function"){
18353                    var n = af[id];
18354                    if(n && n.parentNode){
18355                        n.parentNode.removeChild(n);
18356                    }
18357                }
18358            }
18359         }
18360     },
18361
18362     /**
18363      * Clears the current filter. Note: with the "remove" option
18364      * set a filter cannot be cleared.
18365      */
18366     clear : function(){
18367         var t = this.tree;
18368         var af = this.filtered;
18369         for(var id in af){
18370             if(typeof id != "function"){
18371                 var n = af[id];
18372                 if(n){
18373                     n.ui.show();
18374                 }
18375             }
18376         }
18377         this.filtered = {};
18378     }
18379 };
18380 /*
18381  * Based on:
18382  * Ext JS Library 1.1.1
18383  * Copyright(c) 2006-2007, Ext JS, LLC.
18384  *
18385  * Originally Released Under LGPL - original licence link has changed is not relivant.
18386  *
18387  * Fork - LGPL
18388  * <script type="text/javascript">
18389  */
18390  
18391
18392 /**
18393  * @class Roo.tree.TreeSorter
18394  * Provides sorting of nodes in a TreePanel
18395  * 
18396  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18397  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18398  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18399  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18400  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18401  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18402  * @constructor
18403  * @param {TreePanel} tree
18404  * @param {Object} config
18405  */
18406 Roo.tree.TreeSorter = function(tree, config){
18407     Roo.apply(this, config);
18408     tree.on("beforechildrenrendered", this.doSort, this);
18409     tree.on("append", this.updateSort, this);
18410     tree.on("insert", this.updateSort, this);
18411     
18412     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18413     var p = this.property || "text";
18414     var sortType = this.sortType;
18415     var fs = this.folderSort;
18416     var cs = this.caseSensitive === true;
18417     var leafAttr = this.leafAttr || 'leaf';
18418
18419     this.sortFn = function(n1, n2){
18420         if(fs){
18421             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18422                 return 1;
18423             }
18424             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18425                 return -1;
18426             }
18427         }
18428         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18429         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18430         if(v1 < v2){
18431                         return dsc ? +1 : -1;
18432                 }else if(v1 > v2){
18433                         return dsc ? -1 : +1;
18434         }else{
18435                 return 0;
18436         }
18437     };
18438 };
18439
18440 Roo.tree.TreeSorter.prototype = {
18441     doSort : function(node){
18442         node.sort(this.sortFn);
18443     },
18444     
18445     compareNodes : function(n1, n2){
18446         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18447     },
18448     
18449     updateSort : function(tree, node){
18450         if(node.childrenRendered){
18451             this.doSort.defer(1, this, [node]);
18452         }
18453     }
18454 };/*
18455  * Based on:
18456  * Ext JS Library 1.1.1
18457  * Copyright(c) 2006-2007, Ext JS, LLC.
18458  *
18459  * Originally Released Under LGPL - original licence link has changed is not relivant.
18460  *
18461  * Fork - LGPL
18462  * <script type="text/javascript">
18463  */
18464
18465 if(Roo.dd.DropZone){
18466     
18467 Roo.tree.TreeDropZone = function(tree, config){
18468     this.allowParentInsert = false;
18469     this.allowContainerDrop = false;
18470     this.appendOnly = false;
18471     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18472     this.tree = tree;
18473     this.lastInsertClass = "x-tree-no-status";
18474     this.dragOverData = {};
18475 };
18476
18477 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18478     ddGroup : "TreeDD",
18479     
18480     expandDelay : 1000,
18481     
18482     expandNode : function(node){
18483         if(node.hasChildNodes() && !node.isExpanded()){
18484             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18485         }
18486     },
18487     
18488     queueExpand : function(node){
18489         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18490     },
18491     
18492     cancelExpand : function(){
18493         if(this.expandProcId){
18494             clearTimeout(this.expandProcId);
18495             this.expandProcId = false;
18496         }
18497     },
18498     
18499     isValidDropPoint : function(n, pt, dd, e, data){
18500         if(!n || !data){ return false; }
18501         var targetNode = n.node;
18502         var dropNode = data.node;
18503         // default drop rules
18504         if(!(targetNode && targetNode.isTarget && pt)){
18505             return false;
18506         }
18507         if(pt == "append" && targetNode.allowChildren === false){
18508             return false;
18509         }
18510         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18511             return false;
18512         }
18513         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18514             return false;
18515         }
18516         // reuse the object
18517         var overEvent = this.dragOverData;
18518         overEvent.tree = this.tree;
18519         overEvent.target = targetNode;
18520         overEvent.data = data;
18521         overEvent.point = pt;
18522         overEvent.source = dd;
18523         overEvent.rawEvent = e;
18524         overEvent.dropNode = dropNode;
18525         overEvent.cancel = false;  
18526         var result = this.tree.fireEvent("nodedragover", overEvent);
18527         return overEvent.cancel === false && result !== false;
18528     },
18529     
18530     getDropPoint : function(e, n, dd){
18531         var tn = n.node;
18532         if(tn.isRoot){
18533             return tn.allowChildren !== false ? "append" : false; // always append for root
18534         }
18535         var dragEl = n.ddel;
18536         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18537         var y = Roo.lib.Event.getPageY(e);
18538         var noAppend = tn.allowChildren === false || tn.isLeaf();
18539         if(this.appendOnly || tn.parentNode.allowChildren === false){
18540             return noAppend ? false : "append";
18541         }
18542         var noBelow = false;
18543         if(!this.allowParentInsert){
18544             noBelow = tn.hasChildNodes() && tn.isExpanded();
18545         }
18546         var q = (b - t) / (noAppend ? 2 : 3);
18547         if(y >= t && y < (t + q)){
18548             return "above";
18549         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18550             return "below";
18551         }else{
18552             return "append";
18553         }
18554     },
18555     
18556     onNodeEnter : function(n, dd, e, data){
18557         this.cancelExpand();
18558     },
18559     
18560     onNodeOver : function(n, dd, e, data){
18561         var pt = this.getDropPoint(e, n, dd);
18562         var node = n.node;
18563         
18564         // auto node expand check
18565         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18566             this.queueExpand(node);
18567         }else if(pt != "append"){
18568             this.cancelExpand();
18569         }
18570         
18571         // set the insert point style on the target node
18572         var returnCls = this.dropNotAllowed;
18573         if(this.isValidDropPoint(n, pt, dd, e, data)){
18574            if(pt){
18575                var el = n.ddel;
18576                var cls;
18577                if(pt == "above"){
18578                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18579                    cls = "x-tree-drag-insert-above";
18580                }else if(pt == "below"){
18581                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18582                    cls = "x-tree-drag-insert-below";
18583                }else{
18584                    returnCls = "x-tree-drop-ok-append";
18585                    cls = "x-tree-drag-append";
18586                }
18587                if(this.lastInsertClass != cls){
18588                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18589                    this.lastInsertClass = cls;
18590                }
18591            }
18592        }
18593        return returnCls;
18594     },
18595     
18596     onNodeOut : function(n, dd, e, data){
18597         this.cancelExpand();
18598         this.removeDropIndicators(n);
18599     },
18600     
18601     onNodeDrop : function(n, dd, e, data){
18602         var point = this.getDropPoint(e, n, dd);
18603         var targetNode = n.node;
18604         targetNode.ui.startDrop();
18605         if(!this.isValidDropPoint(n, point, dd, e, data)){
18606             targetNode.ui.endDrop();
18607             return false;
18608         }
18609         // first try to find the drop node
18610         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18611         var dropEvent = {
18612             tree : this.tree,
18613             target: targetNode,
18614             data: data,
18615             point: point,
18616             source: dd,
18617             rawEvent: e,
18618             dropNode: dropNode,
18619             cancel: !dropNode   
18620         };
18621         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18622         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18623             targetNode.ui.endDrop();
18624             return false;
18625         }
18626         // allow target changing
18627         targetNode = dropEvent.target;
18628         if(point == "append" && !targetNode.isExpanded()){
18629             targetNode.expand(false, null, function(){
18630                 this.completeDrop(dropEvent);
18631             }.createDelegate(this));
18632         }else{
18633             this.completeDrop(dropEvent);
18634         }
18635         return true;
18636     },
18637     
18638     completeDrop : function(de){
18639         var ns = de.dropNode, p = de.point, t = de.target;
18640         if(!(ns instanceof Array)){
18641             ns = [ns];
18642         }
18643         var n;
18644         for(var i = 0, len = ns.length; i < len; i++){
18645             n = ns[i];
18646             if(p == "above"){
18647                 t.parentNode.insertBefore(n, t);
18648             }else if(p == "below"){
18649                 t.parentNode.insertBefore(n, t.nextSibling);
18650             }else{
18651                 t.appendChild(n);
18652             }
18653         }
18654         n.ui.focus();
18655         if(this.tree.hlDrop){
18656             n.ui.highlight();
18657         }
18658         t.ui.endDrop();
18659         this.tree.fireEvent("nodedrop", de);
18660     },
18661     
18662     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18663         if(this.tree.hlDrop){
18664             dropNode.ui.focus();
18665             dropNode.ui.highlight();
18666         }
18667         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18668     },
18669     
18670     getTree : function(){
18671         return this.tree;
18672     },
18673     
18674     removeDropIndicators : function(n){
18675         if(n && n.ddel){
18676             var el = n.ddel;
18677             Roo.fly(el).removeClass([
18678                     "x-tree-drag-insert-above",
18679                     "x-tree-drag-insert-below",
18680                     "x-tree-drag-append"]);
18681             this.lastInsertClass = "_noclass";
18682         }
18683     },
18684     
18685     beforeDragDrop : function(target, e, id){
18686         this.cancelExpand();
18687         return true;
18688     },
18689     
18690     afterRepair : function(data){
18691         if(data && Roo.enableFx){
18692             data.node.ui.highlight();
18693         }
18694         this.hideProxy();
18695     }    
18696 });
18697
18698 }/*
18699  * Based on:
18700  * Ext JS Library 1.1.1
18701  * Copyright(c) 2006-2007, Ext JS, LLC.
18702  *
18703  * Originally Released Under LGPL - original licence link has changed is not relivant.
18704  *
18705  * Fork - LGPL
18706  * <script type="text/javascript">
18707  */
18708  
18709
18710 if(Roo.dd.DragZone){
18711 Roo.tree.TreeDragZone = function(tree, config){
18712     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18713     this.tree = tree;
18714 };
18715
18716 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18717     ddGroup : "TreeDD",
18718     
18719     onBeforeDrag : function(data, e){
18720         var n = data.node;
18721         return n && n.draggable && !n.disabled;
18722     },
18723     
18724     onInitDrag : function(e){
18725         var data = this.dragData;
18726         this.tree.getSelectionModel().select(data.node);
18727         this.proxy.update("");
18728         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18729         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18730     },
18731     
18732     getRepairXY : function(e, data){
18733         return data.node.ui.getDDRepairXY();
18734     },
18735     
18736     onEndDrag : function(data, e){
18737         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18738     },
18739     
18740     onValidDrop : function(dd, e, id){
18741         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18742         this.hideProxy();
18743     },
18744     
18745     beforeInvalidDrop : function(e, id){
18746         // this scrolls the original position back into view
18747         var sm = this.tree.getSelectionModel();
18748         sm.clearSelections();
18749         sm.select(this.dragData.node);
18750     }
18751 });
18752 }/*
18753  * Based on:
18754  * Ext JS Library 1.1.1
18755  * Copyright(c) 2006-2007, Ext JS, LLC.
18756  *
18757  * Originally Released Under LGPL - original licence link has changed is not relivant.
18758  *
18759  * Fork - LGPL
18760  * <script type="text/javascript">
18761  */
18762 /**
18763  * @class Roo.tree.TreeEditor
18764  * @extends Roo.Editor
18765  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18766  * as the editor field.
18767  * @constructor
18768  * @param {TreePanel} tree
18769  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18770  */
18771 Roo.tree.TreeEditor = function(tree, config){
18772     config = config || {};
18773     var field = config.events ? config : new Roo.form.TextField(config);
18774     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18775
18776     this.tree = tree;
18777
18778     tree.on('beforeclick', this.beforeNodeClick, this);
18779     tree.getTreeEl().on('mousedown', this.hide, this);
18780     this.on('complete', this.updateNode, this);
18781     this.on('beforestartedit', this.fitToTree, this);
18782     this.on('startedit', this.bindScroll, this, {delay:10});
18783     this.on('specialkey', this.onSpecialKey, this);
18784 };
18785
18786 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18787     /**
18788      * @cfg {String} alignment
18789      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18790      */
18791     alignment: "l-l",
18792     // inherit
18793     autoSize: false,
18794     /**
18795      * @cfg {Boolean} hideEl
18796      * True to hide the bound element while the editor is displayed (defaults to false)
18797      */
18798     hideEl : false,
18799     /**
18800      * @cfg {String} cls
18801      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18802      */
18803     cls: "x-small-editor x-tree-editor",
18804     /**
18805      * @cfg {Boolean} shim
18806      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18807      */
18808     shim:false,
18809     // inherit
18810     shadow:"frame",
18811     /**
18812      * @cfg {Number} maxWidth
18813      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18814      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18815      * scroll and client offsets into account prior to each edit.
18816      */
18817     maxWidth: 250,
18818
18819     editDelay : 350,
18820
18821     // private
18822     fitToTree : function(ed, el){
18823         var td = this.tree.getTreeEl().dom, nd = el.dom;
18824         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18825             td.scrollLeft = nd.offsetLeft;
18826         }
18827         var w = Math.min(
18828                 this.maxWidth,
18829                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18830         this.setSize(w, '');
18831     },
18832
18833     // private
18834     triggerEdit : function(node){
18835         this.completeEdit();
18836         this.editNode = node;
18837         this.startEdit(node.ui.textNode, node.text);
18838     },
18839
18840     // private
18841     bindScroll : function(){
18842         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18843     },
18844
18845     // private
18846     beforeNodeClick : function(node, e){
18847         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18848         this.lastClick = new Date();
18849         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18850             e.stopEvent();
18851             this.triggerEdit(node);
18852             return false;
18853         }
18854     },
18855
18856     // private
18857     updateNode : function(ed, value){
18858         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18859         this.editNode.setText(value);
18860     },
18861
18862     // private
18863     onHide : function(){
18864         Roo.tree.TreeEditor.superclass.onHide.call(this);
18865         if(this.editNode){
18866             this.editNode.ui.focus();
18867         }
18868     },
18869
18870     // private
18871     onSpecialKey : function(field, e){
18872         var k = e.getKey();
18873         if(k == e.ESC){
18874             e.stopEvent();
18875             this.cancelEdit();
18876         }else if(k == e.ENTER && !e.hasModifier()){
18877             e.stopEvent();
18878             this.completeEdit();
18879         }
18880     }
18881 });//<Script type="text/javascript">
18882 /*
18883  * Based on:
18884  * Ext JS Library 1.1.1
18885  * Copyright(c) 2006-2007, Ext JS, LLC.
18886  *
18887  * Originally Released Under LGPL - original licence link has changed is not relivant.
18888  *
18889  * Fork - LGPL
18890  * <script type="text/javascript">
18891  */
18892  
18893 /**
18894  * Not documented??? - probably should be...
18895  */
18896
18897 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18898     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18899     
18900     renderElements : function(n, a, targetNode, bulkRender){
18901         //consel.log("renderElements?");
18902         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18903
18904         var t = n.getOwnerTree();
18905         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18906         
18907         var cols = t.columns;
18908         var bw = t.borderWidth;
18909         var c = cols[0];
18910         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18911          var cb = typeof a.checked == "boolean";
18912         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18913         var colcls = 'x-t-' + tid + '-c0';
18914         var buf = [
18915             '<li class="x-tree-node">',
18916             
18917                 
18918                 '<div class="x-tree-node-el ', a.cls,'">',
18919                     // extran...
18920                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18921                 
18922                 
18923                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18924                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18925                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18926                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18927                            (a.iconCls ? ' '+a.iconCls : ''),
18928                            '" unselectable="on" />',
18929                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18930                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18931                              
18932                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18933                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18934                             '<span unselectable="on" qtip="' + tx + '">',
18935                              tx,
18936                              '</span></a>' ,
18937                     '</div>',
18938                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18939                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18940                  ];
18941         
18942         for(var i = 1, len = cols.length; i < len; i++){
18943             c = cols[i];
18944             colcls = 'x-t-' + tid + '-c' +i;
18945             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18946             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18947                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18948                       "</div>");
18949          }
18950          
18951          buf.push(
18952             '</a>',
18953             '<div class="x-clear"></div></div>',
18954             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18955             "</li>");
18956         
18957         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18958             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18959                                 n.nextSibling.ui.getEl(), buf.join(""));
18960         }else{
18961             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18962         }
18963         var el = this.wrap.firstChild;
18964         this.elRow = el;
18965         this.elNode = el.firstChild;
18966         this.ranchor = el.childNodes[1];
18967         this.ctNode = this.wrap.childNodes[1];
18968         var cs = el.firstChild.childNodes;
18969         this.indentNode = cs[0];
18970         this.ecNode = cs[1];
18971         this.iconNode = cs[2];
18972         var index = 3;
18973         if(cb){
18974             this.checkbox = cs[3];
18975             index++;
18976         }
18977         this.anchor = cs[index];
18978         
18979         this.textNode = cs[index].firstChild;
18980         
18981         //el.on("click", this.onClick, this);
18982         //el.on("dblclick", this.onDblClick, this);
18983         
18984         
18985        // console.log(this);
18986     },
18987     initEvents : function(){
18988         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18989         
18990             
18991         var a = this.ranchor;
18992
18993         var el = Roo.get(a);
18994
18995         if(Roo.isOpera){ // opera render bug ignores the CSS
18996             el.setStyle("text-decoration", "none");
18997         }
18998
18999         el.on("click", this.onClick, this);
19000         el.on("dblclick", this.onDblClick, this);
19001         el.on("contextmenu", this.onContextMenu, this);
19002         
19003     },
19004     
19005     /*onSelectedChange : function(state){
19006         if(state){
19007             this.focus();
19008             this.addClass("x-tree-selected");
19009         }else{
19010             //this.blur();
19011             this.removeClass("x-tree-selected");
19012         }
19013     },*/
19014     addClass : function(cls){
19015         if(this.elRow){
19016             Roo.fly(this.elRow).addClass(cls);
19017         }
19018         
19019     },
19020     
19021     
19022     removeClass : function(cls){
19023         if(this.elRow){
19024             Roo.fly(this.elRow).removeClass(cls);
19025         }
19026     }
19027
19028     
19029     
19030 });//<Script type="text/javascript">
19031
19032 /*
19033  * Based on:
19034  * Ext JS Library 1.1.1
19035  * Copyright(c) 2006-2007, Ext JS, LLC.
19036  *
19037  * Originally Released Under LGPL - original licence link has changed is not relivant.
19038  *
19039  * Fork - LGPL
19040  * <script type="text/javascript">
19041  */
19042  
19043
19044 /**
19045  * @class Roo.tree.ColumnTree
19046  * @extends Roo.data.TreePanel
19047  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19048  * @cfg {int} borderWidth  compined right/left border allowance
19049  * @constructor
19050  * @param {String/HTMLElement/Element} el The container element
19051  * @param {Object} config
19052  */
19053 Roo.tree.ColumnTree =  function(el, config)
19054 {
19055    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19056    this.addEvents({
19057         /**
19058         * @event resize
19059         * Fire this event on a container when it resizes
19060         * @param {int} w Width
19061         * @param {int} h Height
19062         */
19063        "resize" : true
19064     });
19065     this.on('resize', this.onResize, this);
19066 };
19067
19068 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19069     //lines:false,
19070     
19071     
19072     borderWidth: Roo.isBorderBox ? 0 : 2, 
19073     headEls : false,
19074     
19075     render : function(){
19076         // add the header.....
19077        
19078         Roo.tree.ColumnTree.superclass.render.apply(this);
19079         
19080         this.el.addClass('x-column-tree');
19081         
19082         this.headers = this.el.createChild(
19083             {cls:'x-tree-headers'},this.innerCt.dom);
19084    
19085         var cols = this.columns, c;
19086         var totalWidth = 0;
19087         this.headEls = [];
19088         var  len = cols.length;
19089         for(var i = 0; i < len; i++){
19090              c = cols[i];
19091              totalWidth += c.width;
19092             this.headEls.push(this.headers.createChild({
19093                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19094                  cn: {
19095                      cls:'x-tree-hd-text',
19096                      html: c.header
19097                  },
19098                  style:'width:'+(c.width-this.borderWidth)+'px;'
19099              }));
19100         }
19101         this.headers.createChild({cls:'x-clear'});
19102         // prevent floats from wrapping when clipped
19103         this.headers.setWidth(totalWidth);
19104         //this.innerCt.setWidth(totalWidth);
19105         this.innerCt.setStyle({ overflow: 'auto' });
19106         this.onResize(this.width, this.height);
19107              
19108         
19109     },
19110     onResize : function(w,h)
19111     {
19112         this.height = h;
19113         this.width = w;
19114         // resize cols..
19115         this.innerCt.setWidth(this.width);
19116         this.innerCt.setHeight(this.height-20);
19117         
19118         // headers...
19119         var cols = this.columns, c;
19120         var totalWidth = 0;
19121         var expEl = false;
19122         var len = cols.length;
19123         for(var i = 0; i < len; i++){
19124             c = cols[i];
19125             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19126                 // it's the expander..
19127                 expEl  = this.headEls[i];
19128                 continue;
19129             }
19130             totalWidth += c.width;
19131             
19132         }
19133         if (expEl) {
19134             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19135         }
19136         this.headers.setWidth(w-20);
19137
19138         
19139         
19140         
19141     }
19142 });
19143 /*
19144  * Based on:
19145  * Ext JS Library 1.1.1
19146  * Copyright(c) 2006-2007, Ext JS, LLC.
19147  *
19148  * Originally Released Under LGPL - original licence link has changed is not relivant.
19149  *
19150  * Fork - LGPL
19151  * <script type="text/javascript">
19152  */
19153  
19154 /**
19155  * @class Roo.menu.Menu
19156  * @extends Roo.util.Observable
19157  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19158  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19159  * @constructor
19160  * Creates a new Menu
19161  * @param {Object} config Configuration options
19162  */
19163 Roo.menu.Menu = function(config){
19164     Roo.apply(this, config);
19165     this.id = this.id || Roo.id();
19166     this.addEvents({
19167         /**
19168          * @event beforeshow
19169          * Fires before this menu is displayed
19170          * @param {Roo.menu.Menu} this
19171          */
19172         beforeshow : true,
19173         /**
19174          * @event beforehide
19175          * Fires before this menu is hidden
19176          * @param {Roo.menu.Menu} this
19177          */
19178         beforehide : true,
19179         /**
19180          * @event show
19181          * Fires after this menu is displayed
19182          * @param {Roo.menu.Menu} this
19183          */
19184         show : true,
19185         /**
19186          * @event hide
19187          * Fires after this menu is hidden
19188          * @param {Roo.menu.Menu} this
19189          */
19190         hide : true,
19191         /**
19192          * @event click
19193          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19194          * @param {Roo.menu.Menu} this
19195          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19196          * @param {Roo.EventObject} e
19197          */
19198         click : true,
19199         /**
19200          * @event mouseover
19201          * Fires when the mouse is hovering over this menu
19202          * @param {Roo.menu.Menu} this
19203          * @param {Roo.EventObject} e
19204          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19205          */
19206         mouseover : true,
19207         /**
19208          * @event mouseout
19209          * Fires when the mouse exits this menu
19210          * @param {Roo.menu.Menu} this
19211          * @param {Roo.EventObject} e
19212          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19213          */
19214         mouseout : true,
19215         /**
19216          * @event itemclick
19217          * Fires when a menu item contained in this menu is clicked
19218          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19219          * @param {Roo.EventObject} e
19220          */
19221         itemclick: true
19222     });
19223     if (this.registerMenu) {
19224         Roo.menu.MenuMgr.register(this);
19225     }
19226     
19227     var mis = this.items;
19228     this.items = new Roo.util.MixedCollection();
19229     if(mis){
19230         this.add.apply(this, mis);
19231     }
19232 };
19233
19234 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19235     /**
19236      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19237      */
19238     minWidth : 120,
19239     /**
19240      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19241      * for bottom-right shadow (defaults to "sides")
19242      */
19243     shadow : "sides",
19244     /**
19245      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19246      * this menu (defaults to "tl-tr?")
19247      */
19248     subMenuAlign : "tl-tr?",
19249     /**
19250      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19251      * relative to its element of origin (defaults to "tl-bl?")
19252      */
19253     defaultAlign : "tl-bl?",
19254     /**
19255      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19256      */
19257     allowOtherMenus : false,
19258     /**
19259      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19260      */
19261     registerMenu : true,
19262
19263     hidden:true,
19264
19265     // private
19266     render : function(){
19267         if(this.el){
19268             return;
19269         }
19270         var el = this.el = new Roo.Layer({
19271             cls: "x-menu",
19272             shadow:this.shadow,
19273             constrain: false,
19274             parentEl: this.parentEl || document.body,
19275             zindex:15000
19276         });
19277
19278         this.keyNav = new Roo.menu.MenuNav(this);
19279
19280         if(this.plain){
19281             el.addClass("x-menu-plain");
19282         }
19283         if(this.cls){
19284             el.addClass(this.cls);
19285         }
19286         // generic focus element
19287         this.focusEl = el.createChild({
19288             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19289         });
19290         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19291         ul.on("click", this.onClick, this);
19292         ul.on("mouseover", this.onMouseOver, this);
19293         ul.on("mouseout", this.onMouseOut, this);
19294         this.items.each(function(item){
19295             var li = document.createElement("li");
19296             li.className = "x-menu-list-item";
19297             ul.dom.appendChild(li);
19298             item.render(li, this);
19299         }, this);
19300         this.ul = ul;
19301         this.autoWidth();
19302     },
19303
19304     // private
19305     autoWidth : function(){
19306         var el = this.el, ul = this.ul;
19307         if(!el){
19308             return;
19309         }
19310         var w = this.width;
19311         if(w){
19312             el.setWidth(w);
19313         }else if(Roo.isIE){
19314             el.setWidth(this.minWidth);
19315             var t = el.dom.offsetWidth; // force recalc
19316             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19317         }
19318     },
19319
19320     // private
19321     delayAutoWidth : function(){
19322         if(this.rendered){
19323             if(!this.awTask){
19324                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19325             }
19326             this.awTask.delay(20);
19327         }
19328     },
19329
19330     // private
19331     findTargetItem : function(e){
19332         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19333         if(t && t.menuItemId){
19334             return this.items.get(t.menuItemId);
19335         }
19336     },
19337
19338     // private
19339     onClick : function(e){
19340         var t;
19341         if(t = this.findTargetItem(e)){
19342             t.onClick(e);
19343             this.fireEvent("click", this, t, e);
19344         }
19345     },
19346
19347     // private
19348     setActiveItem : function(item, autoExpand){
19349         if(item != this.activeItem){
19350             if(this.activeItem){
19351                 this.activeItem.deactivate();
19352             }
19353             this.activeItem = item;
19354             item.activate(autoExpand);
19355         }else if(autoExpand){
19356             item.expandMenu();
19357         }
19358     },
19359
19360     // private
19361     tryActivate : function(start, step){
19362         var items = this.items;
19363         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19364             var item = items.get(i);
19365             if(!item.disabled && item.canActivate){
19366                 this.setActiveItem(item, false);
19367                 return item;
19368             }
19369         }
19370         return false;
19371     },
19372
19373     // private
19374     onMouseOver : function(e){
19375         var t;
19376         if(t = this.findTargetItem(e)){
19377             if(t.canActivate && !t.disabled){
19378                 this.setActiveItem(t, true);
19379             }
19380         }
19381         this.fireEvent("mouseover", this, e, t);
19382     },
19383
19384     // private
19385     onMouseOut : function(e){
19386         var t;
19387         if(t = this.findTargetItem(e)){
19388             if(t == this.activeItem && t.shouldDeactivate(e)){
19389                 this.activeItem.deactivate();
19390                 delete this.activeItem;
19391             }
19392         }
19393         this.fireEvent("mouseout", this, e, t);
19394     },
19395
19396     /**
19397      * Read-only.  Returns true if the menu is currently displayed, else false.
19398      * @type Boolean
19399      */
19400     isVisible : function(){
19401         return this.el && !this.hidden;
19402     },
19403
19404     /**
19405      * Displays this menu relative to another element
19406      * @param {String/HTMLElement/Roo.Element} element The element to align to
19407      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19408      * the element (defaults to this.defaultAlign)
19409      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19410      */
19411     show : function(el, pos, parentMenu){
19412         this.parentMenu = parentMenu;
19413         if(!this.el){
19414             this.render();
19415         }
19416         this.fireEvent("beforeshow", this);
19417         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19418     },
19419
19420     /**
19421      * Displays this menu at a specific xy position
19422      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19423      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19424      */
19425     showAt : function(xy, parentMenu, /* private: */_e){
19426         this.parentMenu = parentMenu;
19427         if(!this.el){
19428             this.render();
19429         }
19430         if(_e !== false){
19431             this.fireEvent("beforeshow", this);
19432             xy = this.el.adjustForConstraints(xy);
19433         }
19434         this.el.setXY(xy);
19435         this.el.show();
19436         this.hidden = false;
19437         this.focus();
19438         this.fireEvent("show", this);
19439     },
19440
19441     focus : function(){
19442         if(!this.hidden){
19443             this.doFocus.defer(50, this);
19444         }
19445     },
19446
19447     doFocus : function(){
19448         if(!this.hidden){
19449             this.focusEl.focus();
19450         }
19451     },
19452
19453     /**
19454      * Hides this menu and optionally all parent menus
19455      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19456      */
19457     hide : function(deep){
19458         if(this.el && this.isVisible()){
19459             this.fireEvent("beforehide", this);
19460             if(this.activeItem){
19461                 this.activeItem.deactivate();
19462                 this.activeItem = null;
19463             }
19464             this.el.hide();
19465             this.hidden = true;
19466             this.fireEvent("hide", this);
19467         }
19468         if(deep === true && this.parentMenu){
19469             this.parentMenu.hide(true);
19470         }
19471     },
19472
19473     /**
19474      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19475      * Any of the following are valid:
19476      * <ul>
19477      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19478      * <li>An HTMLElement object which will be converted to a menu item</li>
19479      * <li>A menu item config object that will be created as a new menu item</li>
19480      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19481      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19482      * </ul>
19483      * Usage:
19484      * <pre><code>
19485 // Create the menu
19486 var menu = new Roo.menu.Menu();
19487
19488 // Create a menu item to add by reference
19489 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19490
19491 // Add a bunch of items at once using different methods.
19492 // Only the last item added will be returned.
19493 var item = menu.add(
19494     menuItem,                // add existing item by ref
19495     'Dynamic Item',          // new TextItem
19496     '-',                     // new separator
19497     { text: 'Config Item' }  // new item by config
19498 );
19499 </code></pre>
19500      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19501      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19502      */
19503     add : function(){
19504         var a = arguments, l = a.length, item;
19505         for(var i = 0; i < l; i++){
19506             var el = a[i];
19507             if(el.render){ // some kind of Item
19508                 item = this.addItem(el);
19509             }else if(typeof el == "string"){ // string
19510                 if(el == "separator" || el == "-"){
19511                     item = this.addSeparator();
19512                 }else{
19513                     item = this.addText(el);
19514                 }
19515             }else if(el.tagName || el.el){ // element
19516                 item = this.addElement(el);
19517             }else if(typeof el == "object"){ // must be menu item config?
19518                 item = this.addMenuItem(el);
19519             }
19520         }
19521         return item;
19522     },
19523
19524     /**
19525      * Returns this menu's underlying {@link Roo.Element} object
19526      * @return {Roo.Element} The element
19527      */
19528     getEl : function(){
19529         if(!this.el){
19530             this.render();
19531         }
19532         return this.el;
19533     },
19534
19535     /**
19536      * Adds a separator bar to the menu
19537      * @return {Roo.menu.Item} The menu item that was added
19538      */
19539     addSeparator : function(){
19540         return this.addItem(new Roo.menu.Separator());
19541     },
19542
19543     /**
19544      * Adds an {@link Roo.Element} object to the menu
19545      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19546      * @return {Roo.menu.Item} The menu item that was added
19547      */
19548     addElement : function(el){
19549         return this.addItem(new Roo.menu.BaseItem(el));
19550     },
19551
19552     /**
19553      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19554      * @param {Roo.menu.Item} item The menu item to add
19555      * @return {Roo.menu.Item} The menu item that was added
19556      */
19557     addItem : function(item){
19558         this.items.add(item);
19559         if(this.ul){
19560             var li = document.createElement("li");
19561             li.className = "x-menu-list-item";
19562             this.ul.dom.appendChild(li);
19563             item.render(li, this);
19564             this.delayAutoWidth();
19565         }
19566         return item;
19567     },
19568
19569     /**
19570      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19571      * @param {Object} config A MenuItem config object
19572      * @return {Roo.menu.Item} The menu item that was added
19573      */
19574     addMenuItem : function(config){
19575         if(!(config instanceof Roo.menu.Item)){
19576             if(typeof config.checked == "boolean"){ // must be check menu item config?
19577                 config = new Roo.menu.CheckItem(config);
19578             }else{
19579                 config = new Roo.menu.Item(config);
19580             }
19581         }
19582         return this.addItem(config);
19583     },
19584
19585     /**
19586      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19587      * @param {String} text The text to display in the menu item
19588      * @return {Roo.menu.Item} The menu item that was added
19589      */
19590     addText : function(text){
19591         return this.addItem(new Roo.menu.TextItem(text));
19592     },
19593
19594     /**
19595      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19596      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19597      * @param {Roo.menu.Item} item The menu item to add
19598      * @return {Roo.menu.Item} The menu item that was added
19599      */
19600     insert : function(index, item){
19601         this.items.insert(index, item);
19602         if(this.ul){
19603             var li = document.createElement("li");
19604             li.className = "x-menu-list-item";
19605             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19606             item.render(li, this);
19607             this.delayAutoWidth();
19608         }
19609         return item;
19610     },
19611
19612     /**
19613      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19614      * @param {Roo.menu.Item} item The menu item to remove
19615      */
19616     remove : function(item){
19617         this.items.removeKey(item.id);
19618         item.destroy();
19619     },
19620
19621     /**
19622      * Removes and destroys all items in the menu
19623      */
19624     removeAll : function(){
19625         var f;
19626         while(f = this.items.first()){
19627             this.remove(f);
19628         }
19629     }
19630 });
19631
19632 // MenuNav is a private utility class used internally by the Menu
19633 Roo.menu.MenuNav = function(menu){
19634     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19635     this.scope = this.menu = menu;
19636 };
19637
19638 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19639     doRelay : function(e, h){
19640         var k = e.getKey();
19641         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19642             this.menu.tryActivate(0, 1);
19643             return false;
19644         }
19645         return h.call(this.scope || this, e, this.menu);
19646     },
19647
19648     up : function(e, m){
19649         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19650             m.tryActivate(m.items.length-1, -1);
19651         }
19652     },
19653
19654     down : function(e, m){
19655         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19656             m.tryActivate(0, 1);
19657         }
19658     },
19659
19660     right : function(e, m){
19661         if(m.activeItem){
19662             m.activeItem.expandMenu(true);
19663         }
19664     },
19665
19666     left : function(e, m){
19667         m.hide();
19668         if(m.parentMenu && m.parentMenu.activeItem){
19669             m.parentMenu.activeItem.activate();
19670         }
19671     },
19672
19673     enter : function(e, m){
19674         if(m.activeItem){
19675             e.stopPropagation();
19676             m.activeItem.onClick(e);
19677             m.fireEvent("click", this, m.activeItem);
19678             return true;
19679         }
19680     }
19681 });/*
19682  * Based on:
19683  * Ext JS Library 1.1.1
19684  * Copyright(c) 2006-2007, Ext JS, LLC.
19685  *
19686  * Originally Released Under LGPL - original licence link has changed is not relivant.
19687  *
19688  * Fork - LGPL
19689  * <script type="text/javascript">
19690  */
19691  
19692 /**
19693  * @class Roo.menu.MenuMgr
19694  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19695  * @singleton
19696  */
19697 Roo.menu.MenuMgr = function(){
19698    var menus, active, groups = {}, attached = false, lastShow = new Date();
19699
19700    // private - called when first menu is created
19701    function init(){
19702        menus = {};
19703        active = new Roo.util.MixedCollection();
19704        Roo.get(document).addKeyListener(27, function(){
19705            if(active.length > 0){
19706                hideAll();
19707            }
19708        });
19709    }
19710
19711    // private
19712    function hideAll(){
19713        if(active && active.length > 0){
19714            var c = active.clone();
19715            c.each(function(m){
19716                m.hide();
19717            });
19718        }
19719    }
19720
19721    // private
19722    function onHide(m){
19723        active.remove(m);
19724        if(active.length < 1){
19725            Roo.get(document).un("mousedown", onMouseDown);
19726            attached = false;
19727        }
19728    }
19729
19730    // private
19731    function onShow(m){
19732        var last = active.last();
19733        lastShow = new Date();
19734        active.add(m);
19735        if(!attached){
19736            Roo.get(document).on("mousedown", onMouseDown);
19737            attached = true;
19738        }
19739        if(m.parentMenu){
19740           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19741           m.parentMenu.activeChild = m;
19742        }else if(last && last.isVisible()){
19743           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19744        }
19745    }
19746
19747    // private
19748    function onBeforeHide(m){
19749        if(m.activeChild){
19750            m.activeChild.hide();
19751        }
19752        if(m.autoHideTimer){
19753            clearTimeout(m.autoHideTimer);
19754            delete m.autoHideTimer;
19755        }
19756    }
19757
19758    // private
19759    function onBeforeShow(m){
19760        var pm = m.parentMenu;
19761        if(!pm && !m.allowOtherMenus){
19762            hideAll();
19763        }else if(pm && pm.activeChild && active != m){
19764            pm.activeChild.hide();
19765        }
19766    }
19767
19768    // private
19769    function onMouseDown(e){
19770        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19771            hideAll();
19772        }
19773    }
19774
19775    // private
19776    function onBeforeCheck(mi, state){
19777        if(state){
19778            var g = groups[mi.group];
19779            for(var i = 0, l = g.length; i < l; i++){
19780                if(g[i] != mi){
19781                    g[i].setChecked(false);
19782                }
19783            }
19784        }
19785    }
19786
19787    return {
19788
19789        /**
19790         * Hides all menus that are currently visible
19791         */
19792        hideAll : function(){
19793             hideAll();  
19794        },
19795
19796        // private
19797        register : function(menu){
19798            if(!menus){
19799                init();
19800            }
19801            menus[menu.id] = menu;
19802            menu.on("beforehide", onBeforeHide);
19803            menu.on("hide", onHide);
19804            menu.on("beforeshow", onBeforeShow);
19805            menu.on("show", onShow);
19806            var g = menu.group;
19807            if(g && menu.events["checkchange"]){
19808                if(!groups[g]){
19809                    groups[g] = [];
19810                }
19811                groups[g].push(menu);
19812                menu.on("checkchange", onCheck);
19813            }
19814        },
19815
19816         /**
19817          * Returns a {@link Roo.menu.Menu} object
19818          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19819          * be used to generate and return a new Menu instance.
19820          */
19821        get : function(menu){
19822            if(typeof menu == "string"){ // menu id
19823                return menus[menu];
19824            }else if(menu.events){  // menu instance
19825                return menu;
19826            }else if(typeof menu.length == 'number'){ // array of menu items?
19827                return new Roo.menu.Menu({items:menu});
19828            }else{ // otherwise, must be a config
19829                return new Roo.menu.Menu(menu);
19830            }
19831        },
19832
19833        // private
19834        unregister : function(menu){
19835            delete menus[menu.id];
19836            menu.un("beforehide", onBeforeHide);
19837            menu.un("hide", onHide);
19838            menu.un("beforeshow", onBeforeShow);
19839            menu.un("show", onShow);
19840            var g = menu.group;
19841            if(g && menu.events["checkchange"]){
19842                groups[g].remove(menu);
19843                menu.un("checkchange", onCheck);
19844            }
19845        },
19846
19847        // private
19848        registerCheckable : function(menuItem){
19849            var g = menuItem.group;
19850            if(g){
19851                if(!groups[g]){
19852                    groups[g] = [];
19853                }
19854                groups[g].push(menuItem);
19855                menuItem.on("beforecheckchange", onBeforeCheck);
19856            }
19857        },
19858
19859        // private
19860        unregisterCheckable : function(menuItem){
19861            var g = menuItem.group;
19862            if(g){
19863                groups[g].remove(menuItem);
19864                menuItem.un("beforecheckchange", onBeforeCheck);
19865            }
19866        }
19867    };
19868 }();/*
19869  * Based on:
19870  * Ext JS Library 1.1.1
19871  * Copyright(c) 2006-2007, Ext JS, LLC.
19872  *
19873  * Originally Released Under LGPL - original licence link has changed is not relivant.
19874  *
19875  * Fork - LGPL
19876  * <script type="text/javascript">
19877  */
19878  
19879
19880 /**
19881  * @class Roo.menu.BaseItem
19882  * @extends Roo.Component
19883  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19884  * management and base configuration options shared by all menu components.
19885  * @constructor
19886  * Creates a new BaseItem
19887  * @param {Object} config Configuration options
19888  */
19889 Roo.menu.BaseItem = function(config){
19890     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19891
19892     this.addEvents({
19893         /**
19894          * @event click
19895          * Fires when this item is clicked
19896          * @param {Roo.menu.BaseItem} this
19897          * @param {Roo.EventObject} e
19898          */
19899         click: true,
19900         /**
19901          * @event activate
19902          * Fires when this item is activated
19903          * @param {Roo.menu.BaseItem} this
19904          */
19905         activate : true,
19906         /**
19907          * @event deactivate
19908          * Fires when this item is deactivated
19909          * @param {Roo.menu.BaseItem} this
19910          */
19911         deactivate : true
19912     });
19913
19914     if(this.handler){
19915         this.on("click", this.handler, this.scope, true);
19916     }
19917 };
19918
19919 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19920     /**
19921      * @cfg {Function} handler
19922      * A function that will handle the click event of this menu item (defaults to undefined)
19923      */
19924     /**
19925      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19926      */
19927     canActivate : false,
19928     /**
19929      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19930      */
19931     activeClass : "x-menu-item-active",
19932     /**
19933      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19934      */
19935     hideOnClick : true,
19936     /**
19937      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19938      */
19939     hideDelay : 100,
19940
19941     // private
19942     ctype: "Roo.menu.BaseItem",
19943
19944     // private
19945     actionMode : "container",
19946
19947     // private
19948     render : function(container, parentMenu){
19949         this.parentMenu = parentMenu;
19950         Roo.menu.BaseItem.superclass.render.call(this, container);
19951         this.container.menuItemId = this.id;
19952     },
19953
19954     // private
19955     onRender : function(container, position){
19956         this.el = Roo.get(this.el);
19957         container.dom.appendChild(this.el.dom);
19958     },
19959
19960     // private
19961     onClick : function(e){
19962         if(!this.disabled && this.fireEvent("click", this, e) !== false
19963                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19964             this.handleClick(e);
19965         }else{
19966             e.stopEvent();
19967         }
19968     },
19969
19970     // private
19971     activate : function(){
19972         if(this.disabled){
19973             return false;
19974         }
19975         var li = this.container;
19976         li.addClass(this.activeClass);
19977         this.region = li.getRegion().adjust(2, 2, -2, -2);
19978         this.fireEvent("activate", this);
19979         return true;
19980     },
19981
19982     // private
19983     deactivate : function(){
19984         this.container.removeClass(this.activeClass);
19985         this.fireEvent("deactivate", this);
19986     },
19987
19988     // private
19989     shouldDeactivate : function(e){
19990         return !this.region || !this.region.contains(e.getPoint());
19991     },
19992
19993     // private
19994     handleClick : function(e){
19995         if(this.hideOnClick){
19996             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19997         }
19998     },
19999
20000     // private
20001     expandMenu : function(autoActivate){
20002         // do nothing
20003     },
20004
20005     // private
20006     hideMenu : function(){
20007         // do nothing
20008     }
20009 });/*
20010  * Based on:
20011  * Ext JS Library 1.1.1
20012  * Copyright(c) 2006-2007, Ext JS, LLC.
20013  *
20014  * Originally Released Under LGPL - original licence link has changed is not relivant.
20015  *
20016  * Fork - LGPL
20017  * <script type="text/javascript">
20018  */
20019  
20020 /**
20021  * @class Roo.menu.Adapter
20022  * @extends Roo.menu.BaseItem
20023  * 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.
20024  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20025  * @constructor
20026  * Creates a new Adapter
20027  * @param {Object} config Configuration options
20028  */
20029 Roo.menu.Adapter = function(component, config){
20030     Roo.menu.Adapter.superclass.constructor.call(this, config);
20031     this.component = component;
20032 };
20033 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20034     // private
20035     canActivate : true,
20036
20037     // private
20038     onRender : function(container, position){
20039         this.component.render(container);
20040         this.el = this.component.getEl();
20041     },
20042
20043     // private
20044     activate : function(){
20045         if(this.disabled){
20046             return false;
20047         }
20048         this.component.focus();
20049         this.fireEvent("activate", this);
20050         return true;
20051     },
20052
20053     // private
20054     deactivate : function(){
20055         this.fireEvent("deactivate", this);
20056     },
20057
20058     // private
20059     disable : function(){
20060         this.component.disable();
20061         Roo.menu.Adapter.superclass.disable.call(this);
20062     },
20063
20064     // private
20065     enable : function(){
20066         this.component.enable();
20067         Roo.menu.Adapter.superclass.enable.call(this);
20068     }
20069 });/*
20070  * Based on:
20071  * Ext JS Library 1.1.1
20072  * Copyright(c) 2006-2007, Ext JS, LLC.
20073  *
20074  * Originally Released Under LGPL - original licence link has changed is not relivant.
20075  *
20076  * Fork - LGPL
20077  * <script type="text/javascript">
20078  */
20079
20080 /**
20081  * @class Roo.menu.TextItem
20082  * @extends Roo.menu.BaseItem
20083  * Adds a static text string to a menu, usually used as either a heading or group separator.
20084  * @constructor
20085  * Creates a new TextItem
20086  * @param {String} text The text to display
20087  */
20088 Roo.menu.TextItem = function(text){
20089     this.text = text;
20090     Roo.menu.TextItem.superclass.constructor.call(this);
20091 };
20092
20093 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20094     /**
20095      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20096      */
20097     hideOnClick : false,
20098     /**
20099      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20100      */
20101     itemCls : "x-menu-text",
20102
20103     // private
20104     onRender : function(){
20105         var s = document.createElement("span");
20106         s.className = this.itemCls;
20107         s.innerHTML = this.text;
20108         this.el = s;
20109         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20110     }
20111 });/*
20112  * Based on:
20113  * Ext JS Library 1.1.1
20114  * Copyright(c) 2006-2007, Ext JS, LLC.
20115  *
20116  * Originally Released Under LGPL - original licence link has changed is not relivant.
20117  *
20118  * Fork - LGPL
20119  * <script type="text/javascript">
20120  */
20121
20122 /**
20123  * @class Roo.menu.Separator
20124  * @extends Roo.menu.BaseItem
20125  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20126  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20127  * @constructor
20128  * @param {Object} config Configuration options
20129  */
20130 Roo.menu.Separator = function(config){
20131     Roo.menu.Separator.superclass.constructor.call(this, config);
20132 };
20133
20134 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20135     /**
20136      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20137      */
20138     itemCls : "x-menu-sep",
20139     /**
20140      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20141      */
20142     hideOnClick : false,
20143
20144     // private
20145     onRender : function(li){
20146         var s = document.createElement("span");
20147         s.className = this.itemCls;
20148         s.innerHTML = "&#160;";
20149         this.el = s;
20150         li.addClass("x-menu-sep-li");
20151         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20152     }
20153 });/*
20154  * Based on:
20155  * Ext JS Library 1.1.1
20156  * Copyright(c) 2006-2007, Ext JS, LLC.
20157  *
20158  * Originally Released Under LGPL - original licence link has changed is not relivant.
20159  *
20160  * Fork - LGPL
20161  * <script type="text/javascript">
20162  */
20163 /**
20164  * @class Roo.menu.Item
20165  * @extends Roo.menu.BaseItem
20166  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20167  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20168  * activation and click handling.
20169  * @constructor
20170  * Creates a new Item
20171  * @param {Object} config Configuration options
20172  */
20173 Roo.menu.Item = function(config){
20174     Roo.menu.Item.superclass.constructor.call(this, config);
20175     if(this.menu){
20176         this.menu = Roo.menu.MenuMgr.get(this.menu);
20177     }
20178 };
20179 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20180     /**
20181      * @cfg {String} icon
20182      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20183      */
20184     /**
20185      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20186      */
20187     itemCls : "x-menu-item",
20188     /**
20189      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20190      */
20191     canActivate : true,
20192     /**
20193      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20194      */
20195     showDelay: 200,
20196     // doc'd in BaseItem
20197     hideDelay: 200,
20198
20199     // private
20200     ctype: "Roo.menu.Item",
20201     
20202     // private
20203     onRender : function(container, position){
20204         var el = document.createElement("a");
20205         el.hideFocus = true;
20206         el.unselectable = "on";
20207         el.href = this.href || "#";
20208         if(this.hrefTarget){
20209             el.target = this.hrefTarget;
20210         }
20211         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20212         el.innerHTML = String.format(
20213                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
20214                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
20215         this.el = el;
20216         Roo.menu.Item.superclass.onRender.call(this, container, position);
20217     },
20218
20219     /**
20220      * Sets the text to display in this menu item
20221      * @param {String} text The text to display
20222      */
20223     setText : function(text){
20224         this.text = text;
20225         if(this.rendered){
20226             this.el.update(String.format(
20227                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
20228                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20229             this.parentMenu.autoWidth();
20230         }
20231     },
20232
20233     // private
20234     handleClick : function(e){
20235         if(!this.href){ // if no link defined, stop the event automatically
20236             e.stopEvent();
20237         }
20238         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20239     },
20240
20241     // private
20242     activate : function(autoExpand){
20243         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20244             this.focus();
20245             if(autoExpand){
20246                 this.expandMenu();
20247             }
20248         }
20249         return true;
20250     },
20251
20252     // private
20253     shouldDeactivate : function(e){
20254         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20255             if(this.menu && this.menu.isVisible()){
20256                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20257             }
20258             return true;
20259         }
20260         return false;
20261     },
20262
20263     // private
20264     deactivate : function(){
20265         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20266         this.hideMenu();
20267     },
20268
20269     // private
20270     expandMenu : function(autoActivate){
20271         if(!this.disabled && this.menu){
20272             clearTimeout(this.hideTimer);
20273             delete this.hideTimer;
20274             if(!this.menu.isVisible() && !this.showTimer){
20275                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20276             }else if (this.menu.isVisible() && autoActivate){
20277                 this.menu.tryActivate(0, 1);
20278             }
20279         }
20280     },
20281
20282     // private
20283     deferExpand : function(autoActivate){
20284         delete this.showTimer;
20285         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20286         if(autoActivate){
20287             this.menu.tryActivate(0, 1);
20288         }
20289     },
20290
20291     // private
20292     hideMenu : function(){
20293         clearTimeout(this.showTimer);
20294         delete this.showTimer;
20295         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20296             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20297         }
20298     },
20299
20300     // private
20301     deferHide : function(){
20302         delete this.hideTimer;
20303         this.menu.hide();
20304     }
20305 });/*
20306  * Based on:
20307  * Ext JS Library 1.1.1
20308  * Copyright(c) 2006-2007, Ext JS, LLC.
20309  *
20310  * Originally Released Under LGPL - original licence link has changed is not relivant.
20311  *
20312  * Fork - LGPL
20313  * <script type="text/javascript">
20314  */
20315  
20316 /**
20317  * @class Roo.menu.CheckItem
20318  * @extends Roo.menu.Item
20319  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20320  * @constructor
20321  * Creates a new CheckItem
20322  * @param {Object} config Configuration options
20323  */
20324 Roo.menu.CheckItem = function(config){
20325     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20326     this.addEvents({
20327         /**
20328          * @event beforecheckchange
20329          * Fires before the checked value is set, providing an opportunity to cancel if needed
20330          * @param {Roo.menu.CheckItem} this
20331          * @param {Boolean} checked The new checked value that will be set
20332          */
20333         "beforecheckchange" : true,
20334         /**
20335          * @event checkchange
20336          * Fires after the checked value has been set
20337          * @param {Roo.menu.CheckItem} this
20338          * @param {Boolean} checked The checked value that was set
20339          */
20340         "checkchange" : true
20341     });
20342     if(this.checkHandler){
20343         this.on('checkchange', this.checkHandler, this.scope);
20344     }
20345 };
20346 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20347     /**
20348      * @cfg {String} group
20349      * All check items with the same group name will automatically be grouped into a single-select
20350      * radio button group (defaults to '')
20351      */
20352     /**
20353      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20354      */
20355     itemCls : "x-menu-item x-menu-check-item",
20356     /**
20357      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20358      */
20359     groupClass : "x-menu-group-item",
20360
20361     /**
20362      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20363      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20364      * initialized with checked = true will be rendered as checked.
20365      */
20366     checked: false,
20367
20368     // private
20369     ctype: "Roo.menu.CheckItem",
20370
20371     // private
20372     onRender : function(c){
20373         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20374         if(this.group){
20375             this.el.addClass(this.groupClass);
20376         }
20377         Roo.menu.MenuMgr.registerCheckable(this);
20378         if(this.checked){
20379             this.checked = false;
20380             this.setChecked(true, true);
20381         }
20382     },
20383
20384     // private
20385     destroy : function(){
20386         if(this.rendered){
20387             Roo.menu.MenuMgr.unregisterCheckable(this);
20388         }
20389         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20390     },
20391
20392     /**
20393      * Set the checked state of this item
20394      * @param {Boolean} checked The new checked value
20395      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20396      */
20397     setChecked : function(state, suppressEvent){
20398         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20399             if(this.container){
20400                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20401             }
20402             this.checked = state;
20403             if(suppressEvent !== true){
20404                 this.fireEvent("checkchange", this, state);
20405             }
20406         }
20407     },
20408
20409     // private
20410     handleClick : function(e){
20411        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20412            this.setChecked(!this.checked);
20413        }
20414        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20415     }
20416 });/*
20417  * Based on:
20418  * Ext JS Library 1.1.1
20419  * Copyright(c) 2006-2007, Ext JS, LLC.
20420  *
20421  * Originally Released Under LGPL - original licence link has changed is not relivant.
20422  *
20423  * Fork - LGPL
20424  * <script type="text/javascript">
20425  */
20426  
20427 /**
20428  * @class Roo.menu.DateItem
20429  * @extends Roo.menu.Adapter
20430  * A menu item that wraps the {@link Roo.DatPicker} component.
20431  * @constructor
20432  * Creates a new DateItem
20433  * @param {Object} config Configuration options
20434  */
20435 Roo.menu.DateItem = function(config){
20436     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20437     /** The Roo.DatePicker object @type Roo.DatePicker */
20438     this.picker = this.component;
20439     this.addEvents({select: true});
20440     
20441     this.picker.on("render", function(picker){
20442         picker.getEl().swallowEvent("click");
20443         picker.container.addClass("x-menu-date-item");
20444     });
20445
20446     this.picker.on("select", this.onSelect, this);
20447 };
20448
20449 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20450     // private
20451     onSelect : function(picker, date){
20452         this.fireEvent("select", this, date, picker);
20453         Roo.menu.DateItem.superclass.handleClick.call(this);
20454     }
20455 });/*
20456  * Based on:
20457  * Ext JS Library 1.1.1
20458  * Copyright(c) 2006-2007, Ext JS, LLC.
20459  *
20460  * Originally Released Under LGPL - original licence link has changed is not relivant.
20461  *
20462  * Fork - LGPL
20463  * <script type="text/javascript">
20464  */
20465  
20466 /**
20467  * @class Roo.menu.ColorItem
20468  * @extends Roo.menu.Adapter
20469  * A menu item that wraps the {@link Roo.ColorPalette} component.
20470  * @constructor
20471  * Creates a new ColorItem
20472  * @param {Object} config Configuration options
20473  */
20474 Roo.menu.ColorItem = function(config){
20475     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20476     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20477     this.palette = this.component;
20478     this.relayEvents(this.palette, ["select"]);
20479     if(this.selectHandler){
20480         this.on('select', this.selectHandler, this.scope);
20481     }
20482 };
20483 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20484  * Based on:
20485  * Ext JS Library 1.1.1
20486  * Copyright(c) 2006-2007, Ext JS, LLC.
20487  *
20488  * Originally Released Under LGPL - original licence link has changed is not relivant.
20489  *
20490  * Fork - LGPL
20491  * <script type="text/javascript">
20492  */
20493  
20494
20495 /**
20496  * @class Roo.menu.DateMenu
20497  * @extends Roo.menu.Menu
20498  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20499  * @constructor
20500  * Creates a new DateMenu
20501  * @param {Object} config Configuration options
20502  */
20503 Roo.menu.DateMenu = function(config){
20504     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20505     this.plain = true;
20506     var di = new Roo.menu.DateItem(config);
20507     this.add(di);
20508     /**
20509      * The {@link Roo.DatePicker} instance for this DateMenu
20510      * @type DatePicker
20511      */
20512     this.picker = di.picker;
20513     /**
20514      * @event select
20515      * @param {DatePicker} picker
20516      * @param {Date} date
20517      */
20518     this.relayEvents(di, ["select"]);
20519
20520     this.on('beforeshow', function(){
20521         if(this.picker){
20522             this.picker.hideMonthPicker(true);
20523         }
20524     }, this);
20525 };
20526 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20527     cls:'x-date-menu'
20528 });/*
20529  * Based on:
20530  * Ext JS Library 1.1.1
20531  * Copyright(c) 2006-2007, Ext JS, LLC.
20532  *
20533  * Originally Released Under LGPL - original licence link has changed is not relivant.
20534  *
20535  * Fork - LGPL
20536  * <script type="text/javascript">
20537  */
20538  
20539
20540 /**
20541  * @class Roo.menu.ColorMenu
20542  * @extends Roo.menu.Menu
20543  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20544  * @constructor
20545  * Creates a new ColorMenu
20546  * @param {Object} config Configuration options
20547  */
20548 Roo.menu.ColorMenu = function(config){
20549     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20550     this.plain = true;
20551     var ci = new Roo.menu.ColorItem(config);
20552     this.add(ci);
20553     /**
20554      * The {@link Roo.ColorPalette} instance for this ColorMenu
20555      * @type ColorPalette
20556      */
20557     this.palette = ci.palette;
20558     /**
20559      * @event select
20560      * @param {ColorPalette} palette
20561      * @param {String} color
20562      */
20563     this.relayEvents(ci, ["select"]);
20564 };
20565 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20566  * Based on:
20567  * Ext JS Library 1.1.1
20568  * Copyright(c) 2006-2007, Ext JS, LLC.
20569  *
20570  * Originally Released Under LGPL - original licence link has changed is not relivant.
20571  *
20572  * Fork - LGPL
20573  * <script type="text/javascript">
20574  */
20575  
20576 /**
20577  * @class Roo.form.Field
20578  * @extends Roo.BoxComponent
20579  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20580  * @constructor
20581  * Creates a new Field
20582  * @param {Object} config Configuration options
20583  */
20584 Roo.form.Field = function(config){
20585     Roo.form.Field.superclass.constructor.call(this, config);
20586 };
20587
20588 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20589     /**
20590      * @cfg {String} fieldLabel Label to use when rendering a form.
20591      */
20592        /**
20593      * @cfg {String} qtip Mouse over tip
20594      */
20595      
20596     /**
20597      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20598      */
20599     invalidClass : "x-form-invalid",
20600     /**
20601      * @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")
20602      */
20603     invalidText : "The value in this field is invalid",
20604     /**
20605      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20606      */
20607     focusClass : "x-form-focus",
20608     /**
20609      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20610       automatic validation (defaults to "keyup").
20611      */
20612     validationEvent : "keyup",
20613     /**
20614      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20615      */
20616     validateOnBlur : true,
20617     /**
20618      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20619      */
20620     validationDelay : 250,
20621     /**
20622      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20623      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20624      */
20625     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20626     /**
20627      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20628      */
20629     fieldClass : "x-form-field",
20630     /**
20631      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20632      *<pre>
20633 Value         Description
20634 -----------   ----------------------------------------------------------------------
20635 qtip          Display a quick tip when the user hovers over the field
20636 title         Display a default browser title attribute popup
20637 under         Add a block div beneath the field containing the error text
20638 side          Add an error icon to the right of the field with a popup on hover
20639 [element id]  Add the error text directly to the innerHTML of the specified element
20640 </pre>
20641      */
20642     msgTarget : 'qtip',
20643     /**
20644      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20645      */
20646     msgFx : 'normal',
20647
20648     /**
20649      * @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.
20650      */
20651     readOnly : false,
20652
20653     /**
20654      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20655      */
20656     disabled : false,
20657
20658     /**
20659      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20660      */
20661     inputType : undefined,
20662     
20663     /**
20664      * @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).
20665          */
20666         tabIndex : undefined,
20667         
20668     // private
20669     isFormField : true,
20670
20671     // private
20672     hasFocus : false,
20673     /**
20674      * @property {Roo.Element} fieldEl
20675      * Element Containing the rendered Field (with label etc.)
20676      */
20677     /**
20678      * @cfg {Mixed} value A value to initialize this field with.
20679      */
20680     value : undefined,
20681
20682     /**
20683      * @cfg {String} name The field's HTML name attribute.
20684      */
20685     /**
20686      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20687      */
20688
20689         // private ??
20690         initComponent : function(){
20691         Roo.form.Field.superclass.initComponent.call(this);
20692         this.addEvents({
20693             /**
20694              * @event focus
20695              * Fires when this field receives input focus.
20696              * @param {Roo.form.Field} this
20697              */
20698             focus : true,
20699             /**
20700              * @event blur
20701              * Fires when this field loses input focus.
20702              * @param {Roo.form.Field} this
20703              */
20704             blur : true,
20705             /**
20706              * @event specialkey
20707              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20708              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20709              * @param {Roo.form.Field} this
20710              * @param {Roo.EventObject} e The event object
20711              */
20712             specialkey : true,
20713             /**
20714              * @event change
20715              * Fires just before the field blurs if the field value has changed.
20716              * @param {Roo.form.Field} this
20717              * @param {Mixed} newValue The new value
20718              * @param {Mixed} oldValue The original value
20719              */
20720             change : true,
20721             /**
20722              * @event invalid
20723              * Fires after the field has been marked as invalid.
20724              * @param {Roo.form.Field} this
20725              * @param {String} msg The validation message
20726              */
20727             invalid : true,
20728             /**
20729              * @event valid
20730              * Fires after the field has been validated with no errors.
20731              * @param {Roo.form.Field} this
20732              */
20733             valid : true
20734         });
20735     },
20736
20737     /**
20738      * Returns the name attribute of the field if available
20739      * @return {String} name The field name
20740      */
20741     getName: function(){
20742          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20743     },
20744
20745     // private
20746     onRender : function(ct, position){
20747         Roo.form.Field.superclass.onRender.call(this, ct, position);
20748         if(!this.el){
20749             var cfg = this.getAutoCreate();
20750             if(!cfg.name){
20751                 cfg.name = this.name || this.id;
20752             }
20753             if(this.inputType){
20754                 cfg.type = this.inputType;
20755             }
20756             this.el = ct.createChild(cfg, position);
20757         }
20758         var type = this.el.dom.type;
20759         if(type){
20760             if(type == 'password'){
20761                 type = 'text';
20762             }
20763             this.el.addClass('x-form-'+type);
20764         }
20765         if(this.readOnly){
20766             this.el.dom.readOnly = true;
20767         }
20768         if(this.tabIndex !== undefined){
20769             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20770         }
20771
20772         this.el.addClass([this.fieldClass, this.cls]);
20773         this.initValue();
20774     },
20775
20776     /**
20777      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20778      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20779      * @return {Roo.form.Field} this
20780      */
20781     applyTo : function(target){
20782         this.allowDomMove = false;
20783         this.el = Roo.get(target);
20784         this.render(this.el.dom.parentNode);
20785         return this;
20786     },
20787
20788     // private
20789     initValue : function(){
20790         if(this.value !== undefined){
20791             this.setValue(this.value);
20792         }else if(this.el.dom.value.length > 0){
20793             this.setValue(this.el.dom.value);
20794         }
20795     },
20796
20797     /**
20798      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20799      */
20800     isDirty : function() {
20801         if(this.disabled) {
20802             return false;
20803         }
20804         return String(this.getValue()) !== String(this.originalValue);
20805     },
20806
20807     // private
20808     afterRender : function(){
20809         Roo.form.Field.superclass.afterRender.call(this);
20810         this.initEvents();
20811     },
20812
20813     // private
20814     fireKey : function(e){
20815         if(e.isNavKeyPress()){
20816             this.fireEvent("specialkey", this, e);
20817         }
20818     },
20819
20820     /**
20821      * Resets the current field value to the originally loaded value and clears any validation messages
20822      */
20823     reset : function(){
20824         this.setValue(this.originalValue);
20825         this.clearInvalid();
20826     },
20827
20828     // private
20829     initEvents : function(){
20830         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
20831         this.el.on("focus", this.onFocus,  this);
20832         this.el.on("blur", this.onBlur,  this);
20833
20834         // reference to original value for reset
20835         this.originalValue = this.getValue();
20836     },
20837
20838     // private
20839     onFocus : function(){
20840         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20841             this.el.addClass(this.focusClass);
20842         }
20843         if(!this.hasFocus){
20844             this.hasFocus = true;
20845             this.startValue = this.getValue();
20846             this.fireEvent("focus", this);
20847         }
20848     },
20849
20850     beforeBlur : Roo.emptyFn,
20851
20852     // private
20853     onBlur : function(){
20854         this.beforeBlur();
20855         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20856             this.el.removeClass(this.focusClass);
20857         }
20858         this.hasFocus = false;
20859         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20860             this.validate();
20861         }
20862         var v = this.getValue();
20863         if(String(v) !== String(this.startValue)){
20864             this.fireEvent('change', this, v, this.startValue);
20865         }
20866         this.fireEvent("blur", this);
20867     },
20868
20869     /**
20870      * Returns whether or not the field value is currently valid
20871      * @param {Boolean} preventMark True to disable marking the field invalid
20872      * @return {Boolean} True if the value is valid, else false
20873      */
20874     isValid : function(preventMark){
20875         if(this.disabled){
20876             return true;
20877         }
20878         var restore = this.preventMark;
20879         this.preventMark = preventMark === true;
20880         var v = this.validateValue(this.processValue(this.getRawValue()));
20881         this.preventMark = restore;
20882         return v;
20883     },
20884
20885     /**
20886      * Validates the field value
20887      * @return {Boolean} True if the value is valid, else false
20888      */
20889     validate : function(){
20890         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20891             this.clearInvalid();
20892             return true;
20893         }
20894         return false;
20895     },
20896
20897     processValue : function(value){
20898         return value;
20899     },
20900
20901     // private
20902     // Subclasses should provide the validation implementation by overriding this
20903     validateValue : function(value){
20904         return true;
20905     },
20906
20907     /**
20908      * Mark this field as invalid
20909      * @param {String} msg The validation message
20910      */
20911     markInvalid : function(msg){
20912         if(!this.rendered || this.preventMark){ // not rendered
20913             return;
20914         }
20915         this.el.addClass(this.invalidClass);
20916         msg = msg || this.invalidText;
20917         switch(this.msgTarget){
20918             case 'qtip':
20919                 this.el.dom.qtip = msg;
20920                 this.el.dom.qclass = 'x-form-invalid-tip';
20921                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20922                     Roo.QuickTips.enable();
20923                 }
20924                 break;
20925             case 'title':
20926                 this.el.dom.title = msg;
20927                 break;
20928             case 'under':
20929                 if(!this.errorEl){
20930                     var elp = this.el.findParent('.x-form-element', 5, true);
20931                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20932                     this.errorEl.setWidth(elp.getWidth(true)-20);
20933                 }
20934                 this.errorEl.update(msg);
20935                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20936                 break;
20937             case 'side':
20938                 if(!this.errorIcon){
20939                     var elp = this.el.findParent('.x-form-element', 5, true);
20940                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20941                 }
20942                 this.alignErrorIcon();
20943                 this.errorIcon.dom.qtip = msg;
20944                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20945                 this.errorIcon.show();
20946                 this.on('resize', this.alignErrorIcon, this);
20947                 break;
20948             default:
20949                 var t = Roo.getDom(this.msgTarget);
20950                 t.innerHTML = msg;
20951                 t.style.display = this.msgDisplay;
20952                 break;
20953         }
20954         this.fireEvent('invalid', this, msg);
20955     },
20956
20957     // private
20958     alignErrorIcon : function(){
20959         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20960     },
20961
20962     /**
20963      * Clear any invalid styles/messages for this field
20964      */
20965     clearInvalid : function(){
20966         if(!this.rendered || this.preventMark){ // not rendered
20967             return;
20968         }
20969         this.el.removeClass(this.invalidClass);
20970         switch(this.msgTarget){
20971             case 'qtip':
20972                 this.el.dom.qtip = '';
20973                 break;
20974             case 'title':
20975                 this.el.dom.title = '';
20976                 break;
20977             case 'under':
20978                 if(this.errorEl){
20979                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
20980                 }
20981                 break;
20982             case 'side':
20983                 if(this.errorIcon){
20984                     this.errorIcon.dom.qtip = '';
20985                     this.errorIcon.hide();
20986                     this.un('resize', this.alignErrorIcon, this);
20987                 }
20988                 break;
20989             default:
20990                 var t = Roo.getDom(this.msgTarget);
20991                 t.innerHTML = '';
20992                 t.style.display = 'none';
20993                 break;
20994         }
20995         this.fireEvent('valid', this);
20996     },
20997
20998     /**
20999      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21000      * @return {Mixed} value The field value
21001      */
21002     getRawValue : function(){
21003         var v = this.el.getValue();
21004         if(v === this.emptyText){
21005             v = '';
21006         }
21007         return v;
21008     },
21009
21010     /**
21011      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21012      * @return {Mixed} value The field value
21013      */
21014     getValue : function(){
21015         var v = this.el.getValue();
21016         if(v === this.emptyText || v === undefined){
21017             v = '';
21018         }
21019         return v;
21020     },
21021
21022     /**
21023      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21024      * @param {Mixed} value The value to set
21025      */
21026     setRawValue : function(v){
21027         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21028     },
21029
21030     /**
21031      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21032      * @param {Mixed} value The value to set
21033      */
21034     setValue : function(v){
21035         this.value = v;
21036         if(this.rendered){
21037             this.el.dom.value = (v === null || v === undefined ? '' : v);
21038             this.validate();
21039         }
21040     },
21041
21042     adjustSize : function(w, h){
21043         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21044         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21045         return s;
21046     },
21047
21048     adjustWidth : function(tag, w){
21049         tag = tag.toLowerCase();
21050         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21051             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21052                 if(tag == 'input'){
21053                     return w + 2;
21054                 }
21055                 if(tag = 'textarea'){
21056                     return w-2;
21057                 }
21058             }else if(Roo.isOpera){
21059                 if(tag == 'input'){
21060                     return w + 2;
21061                 }
21062                 if(tag = 'textarea'){
21063                     return w-2;
21064                 }
21065             }
21066         }
21067         return w;
21068     }
21069 });
21070
21071
21072 // anything other than normal should be considered experimental
21073 Roo.form.Field.msgFx = {
21074     normal : {
21075         show: function(msgEl, f){
21076             msgEl.setDisplayed('block');
21077         },
21078
21079         hide : function(msgEl, f){
21080             msgEl.setDisplayed(false).update('');
21081         }
21082     },
21083
21084     slide : {
21085         show: function(msgEl, f){
21086             msgEl.slideIn('t', {stopFx:true});
21087         },
21088
21089         hide : function(msgEl, f){
21090             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21091         }
21092     },
21093
21094     slideRight : {
21095         show: function(msgEl, f){
21096             msgEl.fixDisplay();
21097             msgEl.alignTo(f.el, 'tl-tr');
21098             msgEl.slideIn('l', {stopFx:true});
21099         },
21100
21101         hide : function(msgEl, f){
21102             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21103         }
21104     }
21105 };/*
21106  * Based on:
21107  * Ext JS Library 1.1.1
21108  * Copyright(c) 2006-2007, Ext JS, LLC.
21109  *
21110  * Originally Released Under LGPL - original licence link has changed is not relivant.
21111  *
21112  * Fork - LGPL
21113  * <script type="text/javascript">
21114  */
21115  
21116
21117 /**
21118  * @class Roo.form.TextField
21119  * @extends Roo.form.Field
21120  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21121  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21122  * @constructor
21123  * Creates a new TextField
21124  * @param {Object} config Configuration options
21125  */
21126 Roo.form.TextField = function(config){
21127     Roo.form.TextField.superclass.constructor.call(this, config);
21128     this.addEvents({
21129         /**
21130          * @event autosize
21131          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21132          * according to the default logic, but this event provides a hook for the developer to apply additional
21133          * logic at runtime to resize the field if needed.
21134              * @param {Roo.form.Field} this This text field
21135              * @param {Number} width The new field width
21136              */
21137         autosize : true
21138     });
21139 };
21140
21141 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21142     /**
21143      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21144      */
21145     grow : false,
21146     /**
21147      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21148      */
21149     growMin : 30,
21150     /**
21151      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21152      */
21153     growMax : 800,
21154     /**
21155      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21156      */
21157     vtype : null,
21158     /**
21159      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21160      */
21161     maskRe : null,
21162     /**
21163      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21164      */
21165     disableKeyFilter : false,
21166     /**
21167      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21168      */
21169     allowBlank : true,
21170     /**
21171      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21172      */
21173     minLength : 0,
21174     /**
21175      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21176      */
21177     maxLength : Number.MAX_VALUE,
21178     /**
21179      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21180      */
21181     minLengthText : "The minimum length for this field is {0}",
21182     /**
21183      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21184      */
21185     maxLengthText : "The maximum length for this field is {0}",
21186     /**
21187      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21188      */
21189     selectOnFocus : false,
21190     /**
21191      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21192      */
21193     blankText : "This field is required",
21194     /**
21195      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21196      * If available, this function will be called only after the basic validators all return true, and will be passed the
21197      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21198      */
21199     validator : null,
21200     /**
21201      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21202      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21203      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21204      */
21205     regex : null,
21206     /**
21207      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21208      */
21209     regexText : "",
21210     /**
21211      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21212      */
21213     emptyText : null,
21214     /**
21215      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21216      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21217      */
21218     emptyClass : 'x-form-empty-field',
21219
21220     // private
21221     initEvents : function(){
21222         Roo.form.TextField.superclass.initEvents.call(this);
21223         if(this.validationEvent == 'keyup'){
21224             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21225             this.el.on('keyup', this.filterValidation, this);
21226         }
21227         else if(this.validationEvent !== false){
21228             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21229         }
21230         if(this.selectOnFocus || this.emptyText){
21231             this.on("focus", this.preFocus, this);
21232             if(this.emptyText){
21233                 this.on('blur', this.postBlur, this);
21234                 this.applyEmptyText();
21235             }
21236         }
21237         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21238             this.el.on("keypress", this.filterKeys, this);
21239         }
21240         if(this.grow){
21241             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21242             this.el.on("click", this.autoSize,  this);
21243         }
21244     },
21245
21246     processValue : function(value){
21247         if(this.stripCharsRe){
21248             var newValue = value.replace(this.stripCharsRe, '');
21249             if(newValue !== value){
21250                 this.setRawValue(newValue);
21251                 return newValue;
21252             }
21253         }
21254         return value;
21255     },
21256
21257     filterValidation : function(e){
21258         if(!e.isNavKeyPress()){
21259             this.validationTask.delay(this.validationDelay);
21260         }
21261     },
21262
21263     // private
21264     onKeyUp : function(e){
21265         if(!e.isNavKeyPress()){
21266             this.autoSize();
21267         }
21268     },
21269
21270     /**
21271      * Resets the current field value to the originally-loaded value and clears any validation messages.
21272      * Also adds emptyText and emptyClass if the original value was blank.
21273      */
21274     reset : function(){
21275         Roo.form.TextField.superclass.reset.call(this);
21276         this.applyEmptyText();
21277     },
21278
21279     applyEmptyText : function(){
21280         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21281             this.setRawValue(this.emptyText);
21282             this.el.addClass(this.emptyClass);
21283         }
21284     },
21285
21286     // private
21287     preFocus : function(){
21288         if(this.emptyText){
21289             if(this.el.dom.value == this.emptyText){
21290                 this.setRawValue('');
21291             }
21292             this.el.removeClass(this.emptyClass);
21293         }
21294         if(this.selectOnFocus){
21295             this.el.dom.select();
21296         }
21297     },
21298
21299     // private
21300     postBlur : function(){
21301         this.applyEmptyText();
21302     },
21303
21304     // private
21305     filterKeys : function(e){
21306         var k = e.getKey();
21307         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21308             return;
21309         }
21310         var c = e.getCharCode(), cc = String.fromCharCode(c);
21311         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21312             return;
21313         }
21314         if(!this.maskRe.test(cc)){
21315             e.stopEvent();
21316         }
21317     },
21318
21319     setValue : function(v){
21320         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21321             this.el.removeClass(this.emptyClass);
21322         }
21323         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21324         this.applyEmptyText();
21325         this.autoSize();
21326     },
21327
21328     /**
21329      * Validates a value according to the field's validation rules and marks the field as invalid
21330      * if the validation fails
21331      * @param {Mixed} value The value to validate
21332      * @return {Boolean} True if the value is valid, else false
21333      */
21334     validateValue : function(value){
21335         if(value.length < 1 || value === this.emptyText){ // if it's blank
21336              if(this.allowBlank){
21337                 this.clearInvalid();
21338                 return true;
21339              }else{
21340                 this.markInvalid(this.blankText);
21341                 return false;
21342              }
21343         }
21344         if(value.length < this.minLength){
21345             this.markInvalid(String.format(this.minLengthText, this.minLength));
21346             return false;
21347         }
21348         if(value.length > this.maxLength){
21349             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21350             return false;
21351         }
21352         if(this.vtype){
21353             var vt = Roo.form.VTypes;
21354             if(!vt[this.vtype](value, this)){
21355                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21356                 return false;
21357             }
21358         }
21359         if(typeof this.validator == "function"){
21360             var msg = this.validator(value);
21361             if(msg !== true){
21362                 this.markInvalid(msg);
21363                 return false;
21364             }
21365         }
21366         if(this.regex && !this.regex.test(value)){
21367             this.markInvalid(this.regexText);
21368             return false;
21369         }
21370         return true;
21371     },
21372
21373     /**
21374      * Selects text in this field
21375      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21376      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21377      */
21378     selectText : function(start, end){
21379         var v = this.getRawValue();
21380         if(v.length > 0){
21381             start = start === undefined ? 0 : start;
21382             end = end === undefined ? v.length : end;
21383             var d = this.el.dom;
21384             if(d.setSelectionRange){
21385                 d.setSelectionRange(start, end);
21386             }else if(d.createTextRange){
21387                 var range = d.createTextRange();
21388                 range.moveStart("character", start);
21389                 range.moveEnd("character", v.length-end);
21390                 range.select();
21391             }
21392         }
21393     },
21394
21395     /**
21396      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21397      * This only takes effect if grow = true, and fires the autosize event.
21398      */
21399     autoSize : function(){
21400         if(!this.grow || !this.rendered){
21401             return;
21402         }
21403         if(!this.metrics){
21404             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21405         }
21406         var el = this.el;
21407         var v = el.dom.value;
21408         var d = document.createElement('div');
21409         d.appendChild(document.createTextNode(v));
21410         v = d.innerHTML;
21411         d = null;
21412         v += "&#160;";
21413         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21414         this.el.setWidth(w);
21415         this.fireEvent("autosize", this, w);
21416     }
21417 });/*
21418  * Based on:
21419  * Ext JS Library 1.1.1
21420  * Copyright(c) 2006-2007, Ext JS, LLC.
21421  *
21422  * Originally Released Under LGPL - original licence link has changed is not relivant.
21423  *
21424  * Fork - LGPL
21425  * <script type="text/javascript">
21426  */
21427  
21428 /**
21429  * @class Roo.form.Hidden
21430  * @extends Roo.form.TextField
21431  * Simple Hidden element used on forms 
21432  * 
21433  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21434  * 
21435  * @constructor
21436  * Creates a new Hidden form element.
21437  * @param {Object} config Configuration options
21438  */
21439
21440
21441
21442 // easy hidden field...
21443 Roo.form.Hidden = function(config){
21444     Roo.form.Hidden.superclass.constructor.call(this, config);
21445 };
21446   
21447 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21448     fieldLabel:      '',
21449     inputType:      'hidden',
21450     width:          50,
21451     allowBlank:     true,
21452     labelSeparator: '',
21453     hidden:         true,
21454     itemCls :       'x-form-item-display-none'
21455
21456
21457 });
21458
21459
21460 /*
21461  * Based on:
21462  * Ext JS Library 1.1.1
21463  * Copyright(c) 2006-2007, Ext JS, LLC.
21464  *
21465  * Originally Released Under LGPL - original licence link has changed is not relivant.
21466  *
21467  * Fork - LGPL
21468  * <script type="text/javascript">
21469  */
21470  
21471 /**
21472  * @class Roo.form.TriggerField
21473  * @extends Roo.form.TextField
21474  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21475  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21476  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21477  * for which you can provide a custom implementation.  For example:
21478  * <pre><code>
21479 var trigger = new Roo.form.TriggerField();
21480 trigger.onTriggerClick = myTriggerFn;
21481 trigger.applyTo('my-field');
21482 </code></pre>
21483  *
21484  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21485  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21486  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21487  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21488  * @constructor
21489  * Create a new TriggerField.
21490  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21491  * to the base TextField)
21492  */
21493 Roo.form.TriggerField = function(config){
21494     this.mimicing = false;
21495     Roo.form.TriggerField.superclass.constructor.call(this, config);
21496 };
21497
21498 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21499     /**
21500      * @cfg {String} triggerClass A CSS class to apply to the trigger
21501      */
21502     /**
21503      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21504      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21505      */
21506     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21507     /**
21508      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21509      */
21510     hideTrigger:false,
21511
21512     /** @cfg {Boolean} grow @hide */
21513     /** @cfg {Number} growMin @hide */
21514     /** @cfg {Number} growMax @hide */
21515
21516     /**
21517      * @hide 
21518      * @method
21519      */
21520     autoSize: Roo.emptyFn,
21521     // private
21522     monitorTab : true,
21523     // private
21524     deferHeight : true,
21525
21526     
21527     actionMode : 'wrap',
21528     // private
21529     onResize : function(w, h){
21530         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21531         if(typeof w == 'number'){
21532             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
21533         }
21534     },
21535
21536     // private
21537     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21538
21539     // private
21540     getResizeEl : function(){
21541         return this.wrap;
21542     },
21543
21544     // private
21545     getPositionEl : function(){
21546         return this.wrap;
21547     },
21548
21549     // private
21550     alignErrorIcon : function(){
21551         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21552     },
21553
21554     // private
21555     onRender : function(ct, position){
21556         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21557         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21558         this.trigger = this.wrap.createChild(this.triggerConfig ||
21559                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21560         if(this.hideTrigger){
21561             this.trigger.setDisplayed(false);
21562         }
21563         this.initTrigger();
21564         if(!this.width){
21565             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21566         }
21567     },
21568
21569     // private
21570     initTrigger : function(){
21571         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21572         this.trigger.addClassOnOver('x-form-trigger-over');
21573         this.trigger.addClassOnClick('x-form-trigger-click');
21574     },
21575
21576     // private
21577     onDestroy : function(){
21578         if(this.trigger){
21579             this.trigger.removeAllListeners();
21580             this.trigger.remove();
21581         }
21582         if(this.wrap){
21583             this.wrap.remove();
21584         }
21585         Roo.form.TriggerField.superclass.onDestroy.call(this);
21586     },
21587
21588     // private
21589     onFocus : function(){
21590         Roo.form.TriggerField.superclass.onFocus.call(this);
21591         if(!this.mimicing){
21592             this.wrap.addClass('x-trigger-wrap-focus');
21593             this.mimicing = true;
21594             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21595             if(this.monitorTab){
21596                 this.el.on("keydown", this.checkTab, this);
21597             }
21598         }
21599     },
21600
21601     // private
21602     checkTab : function(e){
21603         if(e.getKey() == e.TAB){
21604             this.triggerBlur();
21605         }
21606     },
21607
21608     // private
21609     onBlur : function(){
21610         // do nothing
21611     },
21612
21613     // private
21614     mimicBlur : function(e, t){
21615         if(!this.wrap.contains(t) && this.validateBlur()){
21616             this.triggerBlur();
21617         }
21618     },
21619
21620     // private
21621     triggerBlur : function(){
21622         this.mimicing = false;
21623         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21624         if(this.monitorTab){
21625             this.el.un("keydown", this.checkTab, this);
21626         }
21627         this.wrap.removeClass('x-trigger-wrap-focus');
21628         Roo.form.TriggerField.superclass.onBlur.call(this);
21629     },
21630
21631     // private
21632     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21633     validateBlur : function(e, t){
21634         return true;
21635     },
21636
21637     // private
21638     onDisable : function(){
21639         Roo.form.TriggerField.superclass.onDisable.call(this);
21640         if(this.wrap){
21641             this.wrap.addClass('x-item-disabled');
21642         }
21643     },
21644
21645     // private
21646     onEnable : function(){
21647         Roo.form.TriggerField.superclass.onEnable.call(this);
21648         if(this.wrap){
21649             this.wrap.removeClass('x-item-disabled');
21650         }
21651     },
21652
21653     // private
21654     onShow : function(){
21655         var ae = this.getActionEl();
21656         
21657         if(ae){
21658             ae.dom.style.display = '';
21659             ae.dom.style.visibility = 'visible';
21660         }
21661     },
21662
21663     // private
21664     
21665     onHide : function(){
21666         var ae = this.getActionEl();
21667         ae.dom.style.display = 'none';
21668     },
21669
21670     /**
21671      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21672      * by an implementing function.
21673      * @method
21674      * @param {EventObject} e
21675      */
21676     onTriggerClick : Roo.emptyFn
21677 });
21678
21679 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21680 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21681 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21682 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21683     initComponent : function(){
21684         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21685
21686         this.triggerConfig = {
21687             tag:'span', cls:'x-form-twin-triggers', cn:[
21688             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21689             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21690         ]};
21691     },
21692
21693     getTrigger : function(index){
21694         return this.triggers[index];
21695     },
21696
21697     initTrigger : function(){
21698         var ts = this.trigger.select('.x-form-trigger', true);
21699         this.wrap.setStyle('overflow', 'hidden');
21700         var triggerField = this;
21701         ts.each(function(t, all, index){
21702             t.hide = function(){
21703                 var w = triggerField.wrap.getWidth();
21704                 this.dom.style.display = 'none';
21705                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21706             };
21707             t.show = function(){
21708                 var w = triggerField.wrap.getWidth();
21709                 this.dom.style.display = '';
21710                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21711             };
21712             var triggerIndex = 'Trigger'+(index+1);
21713
21714             if(this['hide'+triggerIndex]){
21715                 t.dom.style.display = 'none';
21716             }
21717             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21718             t.addClassOnOver('x-form-trigger-over');
21719             t.addClassOnClick('x-form-trigger-click');
21720         }, this);
21721         this.triggers = ts.elements;
21722     },
21723
21724     onTrigger1Click : Roo.emptyFn,
21725     onTrigger2Click : Roo.emptyFn
21726 });/*
21727  * Based on:
21728  * Ext JS Library 1.1.1
21729  * Copyright(c) 2006-2007, Ext JS, LLC.
21730  *
21731  * Originally Released Under LGPL - original licence link has changed is not relivant.
21732  *
21733  * Fork - LGPL
21734  * <script type="text/javascript">
21735  */
21736  
21737 /**
21738  * @class Roo.form.TextArea
21739  * @extends Roo.form.TextField
21740  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21741  * support for auto-sizing.
21742  * @constructor
21743  * Creates a new TextArea
21744  * @param {Object} config Configuration options
21745  */
21746 Roo.form.TextArea = function(config){
21747     Roo.form.TextArea.superclass.constructor.call(this, config);
21748     // these are provided exchanges for backwards compat
21749     // minHeight/maxHeight were replaced by growMin/growMax to be
21750     // compatible with TextField growing config values
21751     if(this.minHeight !== undefined){
21752         this.growMin = this.minHeight;
21753     }
21754     if(this.maxHeight !== undefined){
21755         this.growMax = this.maxHeight;
21756     }
21757 };
21758
21759 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21760     /**
21761      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21762      */
21763     growMin : 60,
21764     /**
21765      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21766      */
21767     growMax: 1000,
21768     /**
21769      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21770      * in the field (equivalent to setting overflow: hidden, defaults to false)
21771      */
21772     preventScrollbars: false,
21773     /**
21774      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21775      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21776      */
21777
21778     // private
21779     onRender : function(ct, position){
21780         if(!this.el){
21781             this.defaultAutoCreate = {
21782                 tag: "textarea",
21783                 style:"width:300px;height:60px;",
21784                 autocomplete: "off"
21785             };
21786         }
21787         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21788         if(this.grow){
21789             this.textSizeEl = Roo.DomHelper.append(document.body, {
21790                 tag: "pre", cls: "x-form-grow-sizer"
21791             });
21792             if(this.preventScrollbars){
21793                 this.el.setStyle("overflow", "hidden");
21794             }
21795             this.el.setHeight(this.growMin);
21796         }
21797     },
21798
21799     onDestroy : function(){
21800         if(this.textSizeEl){
21801             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21802         }
21803         Roo.form.TextArea.superclass.onDestroy.call(this);
21804     },
21805
21806     // private
21807     onKeyUp : function(e){
21808         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21809             this.autoSize();
21810         }
21811     },
21812
21813     /**
21814      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21815      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21816      */
21817     autoSize : function(){
21818         if(!this.grow || !this.textSizeEl){
21819             return;
21820         }
21821         var el = this.el;
21822         var v = el.dom.value;
21823         var ts = this.textSizeEl;
21824
21825         ts.innerHTML = '';
21826         ts.appendChild(document.createTextNode(v));
21827         v = ts.innerHTML;
21828
21829         Roo.fly(ts).setWidth(this.el.getWidth());
21830         if(v.length < 1){
21831             v = "&#160;&#160;";
21832         }else{
21833             if(Roo.isIE){
21834                 v = v.replace(/\n/g, '<p>&#160;</p>');
21835             }
21836             v += "&#160;\n&#160;";
21837         }
21838         ts.innerHTML = v;
21839         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21840         if(h != this.lastHeight){
21841             this.lastHeight = h;
21842             this.el.setHeight(h);
21843             this.fireEvent("autosize", this, h);
21844         }
21845     }
21846 });/*
21847  * Based on:
21848  * Ext JS Library 1.1.1
21849  * Copyright(c) 2006-2007, Ext JS, LLC.
21850  *
21851  * Originally Released Under LGPL - original licence link has changed is not relivant.
21852  *
21853  * Fork - LGPL
21854  * <script type="text/javascript">
21855  */
21856  
21857
21858 /**
21859  * @class Roo.form.NumberField
21860  * @extends Roo.form.TextField
21861  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21862  * @constructor
21863  * Creates a new NumberField
21864  * @param {Object} config Configuration options
21865  */
21866 Roo.form.NumberField = function(config){
21867     Roo.form.NumberField.superclass.constructor.call(this, config);
21868 };
21869
21870 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21871     /**
21872      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21873      */
21874     fieldClass: "x-form-field x-form-num-field",
21875     /**
21876      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21877      */
21878     allowDecimals : true,
21879     /**
21880      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21881      */
21882     decimalSeparator : ".",
21883     /**
21884      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21885      */
21886     decimalPrecision : 2,
21887     /**
21888      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21889      */
21890     allowNegative : true,
21891     /**
21892      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21893      */
21894     minValue : Number.NEGATIVE_INFINITY,
21895     /**
21896      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21897      */
21898     maxValue : Number.MAX_VALUE,
21899     /**
21900      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21901      */
21902     minText : "The minimum value for this field is {0}",
21903     /**
21904      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21905      */
21906     maxText : "The maximum value for this field is {0}",
21907     /**
21908      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21909      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21910      */
21911     nanText : "{0} is not a valid number",
21912
21913     // private
21914     initEvents : function(){
21915         Roo.form.NumberField.superclass.initEvents.call(this);
21916         var allowed = "0123456789";
21917         if(this.allowDecimals){
21918             allowed += this.decimalSeparator;
21919         }
21920         if(this.allowNegative){
21921             allowed += "-";
21922         }
21923         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21924         var keyPress = function(e){
21925             var k = e.getKey();
21926             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21927                 return;
21928             }
21929             var c = e.getCharCode();
21930             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21931                 e.stopEvent();
21932             }
21933         };
21934         this.el.on("keypress", keyPress, this);
21935     },
21936
21937     // private
21938     validateValue : function(value){
21939         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21940             return false;
21941         }
21942         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21943              return true;
21944         }
21945         var num = this.parseValue(value);
21946         if(isNaN(num)){
21947             this.markInvalid(String.format(this.nanText, value));
21948             return false;
21949         }
21950         if(num < this.minValue){
21951             this.markInvalid(String.format(this.minText, this.minValue));
21952             return false;
21953         }
21954         if(num > this.maxValue){
21955             this.markInvalid(String.format(this.maxText, this.maxValue));
21956             return false;
21957         }
21958         return true;
21959     },
21960
21961     getValue : function(){
21962         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21963     },
21964
21965     // private
21966     parseValue : function(value){
21967         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21968         return isNaN(value) ? '' : value;
21969     },
21970
21971     // private
21972     fixPrecision : function(value){
21973         var nan = isNaN(value);
21974         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
21975             return nan ? '' : value;
21976         }
21977         return parseFloat(value).toFixed(this.decimalPrecision);
21978     },
21979
21980     setValue : function(v){
21981         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
21982     },
21983
21984     // private
21985     decimalPrecisionFcn : function(v){
21986         return Math.floor(v);
21987     },
21988
21989     beforeBlur : function(){
21990         var v = this.parseValue(this.getRawValue());
21991         if(v){
21992             this.setValue(this.fixPrecision(v));
21993         }
21994     }
21995 });/*
21996  * Based on:
21997  * Ext JS Library 1.1.1
21998  * Copyright(c) 2006-2007, Ext JS, LLC.
21999  *
22000  * Originally Released Under LGPL - original licence link has changed is not relivant.
22001  *
22002  * Fork - LGPL
22003  * <script type="text/javascript">
22004  */
22005  
22006 /**
22007  * @class Roo.form.DateField
22008  * @extends Roo.form.TriggerField
22009  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22010 * @constructor
22011 * Create a new DateField
22012 * @param {Object} config
22013  */
22014 Roo.form.DateField = function(config){
22015     Roo.form.DateField.superclass.constructor.call(this, config);
22016     
22017       this.addEvents({
22018          
22019         /**
22020          * @event select
22021          * Fires when a date is selected
22022              * @param {Roo.form.DateField} combo This combo box
22023              * @param {Date} date The date selected
22024              */
22025         'select' : true
22026          
22027     });
22028     
22029     
22030     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22031     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22032     this.ddMatch = null;
22033     if(this.disabledDates){
22034         var dd = this.disabledDates;
22035         var re = "(?:";
22036         for(var i = 0; i < dd.length; i++){
22037             re += dd[i];
22038             if(i != dd.length-1) re += "|";
22039         }
22040         this.ddMatch = new RegExp(re + ")");
22041     }
22042 };
22043
22044 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22045     /**
22046      * @cfg {String} format
22047      * The default date format string which can be overriden for localization support.  The format must be
22048      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22049      */
22050     format : "m/d/y",
22051     /**
22052      * @cfg {String} altFormats
22053      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22054      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22055      */
22056     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22057     /**
22058      * @cfg {Array} disabledDays
22059      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22060      */
22061     disabledDays : null,
22062     /**
22063      * @cfg {String} disabledDaysText
22064      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22065      */
22066     disabledDaysText : "Disabled",
22067     /**
22068      * @cfg {Array} disabledDates
22069      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22070      * expression so they are very powerful. Some examples:
22071      * <ul>
22072      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22073      * <li>["03/08", "09/16"] would disable those days for every year</li>
22074      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22075      * <li>["03/../2006"] would disable every day in March 2006</li>
22076      * <li>["^03"] would disable every day in every March</li>
22077      * </ul>
22078      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22079      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22080      */
22081     disabledDates : null,
22082     /**
22083      * @cfg {String} disabledDatesText
22084      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22085      */
22086     disabledDatesText : "Disabled",
22087     /**
22088      * @cfg {Date/String} minValue
22089      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22090      * valid format (defaults to null).
22091      */
22092     minValue : null,
22093     /**
22094      * @cfg {Date/String} maxValue
22095      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22096      * valid format (defaults to null).
22097      */
22098     maxValue : null,
22099     /**
22100      * @cfg {String} minText
22101      * The error text to display when the date in the cell is before minValue (defaults to
22102      * 'The date in this field must be after {minValue}').
22103      */
22104     minText : "The date in this field must be equal to or after {0}",
22105     /**
22106      * @cfg {String} maxText
22107      * The error text to display when the date in the cell is after maxValue (defaults to
22108      * 'The date in this field must be before {maxValue}').
22109      */
22110     maxText : "The date in this field must be equal to or before {0}",
22111     /**
22112      * @cfg {String} invalidText
22113      * The error text to display when the date in the field is invalid (defaults to
22114      * '{value} is not a valid date - it must be in the format {format}').
22115      */
22116     invalidText : "{0} is not a valid date - it must be in the format {1}",
22117     /**
22118      * @cfg {String} triggerClass
22119      * An additional CSS class used to style the trigger button.  The trigger will always get the
22120      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22121      * which displays a calendar icon).
22122      */
22123     triggerClass : 'x-form-date-trigger',
22124     
22125
22126     /**
22127      * @cfg {bool} useIso
22128      * if enabled, then the date field will use a hidden field to store the 
22129      * real value as iso formated date. default (false)
22130      */ 
22131     useIso : false,
22132     /**
22133      * @cfg {String/Object} autoCreate
22134      * A DomHelper element spec, or true for a default element spec (defaults to
22135      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22136      */ 
22137     // private
22138     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22139     
22140     // private
22141     hiddenField: false,
22142     
22143     onRender : function(ct, position)
22144     {
22145         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22146         if (this.useIso) {
22147             this.el.dom.removeAttribute('name'); 
22148             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22149                     'before', true);
22150             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22151             // prevent input submission
22152             this.hiddenName = this.name;
22153         }
22154             
22155             
22156     },
22157     
22158     // private
22159     validateValue : function(value)
22160     {
22161         value = this.formatDate(value);
22162         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22163             return false;
22164         }
22165         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22166              return true;
22167         }
22168         var svalue = value;
22169         value = this.parseDate(value);
22170         if(!value){
22171             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22172             return false;
22173         }
22174         var time = value.getTime();
22175         if(this.minValue && time < this.minValue.getTime()){
22176             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22177             return false;
22178         }
22179         if(this.maxValue && time > this.maxValue.getTime()){
22180             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22181             return false;
22182         }
22183         if(this.disabledDays){
22184             var day = value.getDay();
22185             for(var i = 0; i < this.disabledDays.length; i++) {
22186                 if(day === this.disabledDays[i]){
22187                     this.markInvalid(this.disabledDaysText);
22188                     return false;
22189                 }
22190             }
22191         }
22192         var fvalue = this.formatDate(value);
22193         if(this.ddMatch && this.ddMatch.test(fvalue)){
22194             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22195             return false;
22196         }
22197         return true;
22198     },
22199
22200     // private
22201     // Provides logic to override the default TriggerField.validateBlur which just returns true
22202     validateBlur : function(){
22203         return !this.menu || !this.menu.isVisible();
22204     },
22205
22206     /**
22207      * Returns the current date value of the date field.
22208      * @return {Date} The date value
22209      */
22210     getValue : function(){
22211         
22212         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22213     },
22214
22215     /**
22216      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22217      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22218      * (the default format used is "m/d/y").
22219      * <br />Usage:
22220      * <pre><code>
22221 //All of these calls set the same date value (May 4, 2006)
22222
22223 //Pass a date object:
22224 var dt = new Date('5/4/06');
22225 dateField.setValue(dt);
22226
22227 //Pass a date string (default format):
22228 dateField.setValue('5/4/06');
22229
22230 //Pass a date string (custom format):
22231 dateField.format = 'Y-m-d';
22232 dateField.setValue('2006-5-4');
22233 </code></pre>
22234      * @param {String/Date} date The date or valid date string
22235      */
22236     setValue : function(date){
22237         if (this.hiddenField) {
22238             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22239         }
22240         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22241     },
22242
22243     // private
22244     parseDate : function(value){
22245         if(!value || value instanceof Date){
22246             return value;
22247         }
22248         var v = Date.parseDate(value, this.format);
22249         if(!v && this.altFormats){
22250             if(!this.altFormatsArray){
22251                 this.altFormatsArray = this.altFormats.split("|");
22252             }
22253             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22254                 v = Date.parseDate(value, this.altFormatsArray[i]);
22255             }
22256         }
22257         return v;
22258     },
22259
22260     // private
22261     formatDate : function(date, fmt){
22262         return (!date || !(date instanceof Date)) ?
22263                date : date.dateFormat(fmt || this.format);
22264     },
22265
22266     // private
22267     menuListeners : {
22268         select: function(m, d){
22269             this.setValue(d);
22270             this.fireEvent('select', this, d);
22271         },
22272         show : function(){ // retain focus styling
22273             this.onFocus();
22274         },
22275         hide : function(){
22276             this.focus.defer(10, this);
22277             var ml = this.menuListeners;
22278             this.menu.un("select", ml.select,  this);
22279             this.menu.un("show", ml.show,  this);
22280             this.menu.un("hide", ml.hide,  this);
22281         }
22282     },
22283
22284     // private
22285     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22286     onTriggerClick : function(){
22287         if(this.disabled){
22288             return;
22289         }
22290         if(this.menu == null){
22291             this.menu = new Roo.menu.DateMenu();
22292         }
22293         Roo.apply(this.menu.picker,  {
22294             showClear: this.allowBlank,
22295             minDate : this.minValue,
22296             maxDate : this.maxValue,
22297             disabledDatesRE : this.ddMatch,
22298             disabledDatesText : this.disabledDatesText,
22299             disabledDays : this.disabledDays,
22300             disabledDaysText : this.disabledDaysText,
22301             format : this.format,
22302             minText : String.format(this.minText, this.formatDate(this.minValue)),
22303             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22304         });
22305         this.menu.on(Roo.apply({}, this.menuListeners, {
22306             scope:this
22307         }));
22308         this.menu.picker.setValue(this.getValue() || new Date());
22309         this.menu.show(this.el, "tl-bl?");
22310     },
22311
22312     beforeBlur : function(){
22313         var v = this.parseDate(this.getRawValue());
22314         if(v){
22315             this.setValue(v);
22316         }
22317     }
22318
22319     /** @cfg {Boolean} grow @hide */
22320     /** @cfg {Number} growMin @hide */
22321     /** @cfg {Number} growMax @hide */
22322     /**
22323      * @hide
22324      * @method autoSize
22325      */
22326 });/*
22327  * Based on:
22328  * Ext JS Library 1.1.1
22329  * Copyright(c) 2006-2007, Ext JS, LLC.
22330  *
22331  * Originally Released Under LGPL - original licence link has changed is not relivant.
22332  *
22333  * Fork - LGPL
22334  * <script type="text/javascript">
22335  */
22336  
22337
22338 /**
22339  * @class Roo.form.ComboBox
22340  * @extends Roo.form.TriggerField
22341  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22342  * @constructor
22343  * Create a new ComboBox.
22344  * @param {Object} config Configuration options
22345  */
22346 Roo.form.ComboBox = function(config){
22347     Roo.form.ComboBox.superclass.constructor.call(this, config);
22348     this.addEvents({
22349         /**
22350          * @event expand
22351          * Fires when the dropdown list is expanded
22352              * @param {Roo.form.ComboBox} combo This combo box
22353              */
22354         'expand' : true,
22355         /**
22356          * @event collapse
22357          * Fires when the dropdown list is collapsed
22358              * @param {Roo.form.ComboBox} combo This combo box
22359              */
22360         'collapse' : true,
22361         /**
22362          * @event beforeselect
22363          * Fires before a list item is selected. Return false to cancel the selection.
22364              * @param {Roo.form.ComboBox} combo This combo box
22365              * @param {Roo.data.Record} record The data record returned from the underlying store
22366              * @param {Number} index The index of the selected item in the dropdown list
22367              */
22368         'beforeselect' : true,
22369         /**
22370          * @event select
22371          * Fires when a list item is selected
22372              * @param {Roo.form.ComboBox} combo This combo box
22373              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22374              * @param {Number} index The index of the selected item in the dropdown list
22375              */
22376         'select' : true,
22377         /**
22378          * @event beforequery
22379          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22380          * The event object passed has these properties:
22381              * @param {Roo.form.ComboBox} combo This combo box
22382              * @param {String} query The query
22383              * @param {Boolean} forceAll true to force "all" query
22384              * @param {Boolean} cancel true to cancel the query
22385              * @param {Object} e The query event object
22386              */
22387         'beforequery': true
22388     });
22389     if(this.transform){
22390         this.allowDomMove = false;
22391         var s = Roo.getDom(this.transform);
22392         if(!this.hiddenName){
22393             this.hiddenName = s.name;
22394         }
22395         if(!this.store){
22396             this.mode = 'local';
22397             var d = [], opts = s.options;
22398             for(var i = 0, len = opts.length;i < len; i++){
22399                 var o = opts[i];
22400                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22401                 if(o.selected) {
22402                     this.value = value;
22403                 }
22404                 d.push([value, o.text]);
22405             }
22406             this.store = new Roo.data.SimpleStore({
22407                 'id': 0,
22408                 fields: ['value', 'text'],
22409                 data : d
22410             });
22411             this.valueField = 'value';
22412             this.displayField = 'text';
22413         }
22414         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22415         if(!this.lazyRender){
22416             this.target = true;
22417             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22418             s.parentNode.removeChild(s); // remove it
22419             this.render(this.el.parentNode);
22420         }else{
22421             s.parentNode.removeChild(s); // remove it
22422         }
22423
22424     }
22425     if (this.store) {
22426         this.store = Roo.factory(this.store, Roo.data);
22427     }
22428     
22429     this.selectedIndex = -1;
22430     if(this.mode == 'local'){
22431         if(config.queryDelay === undefined){
22432             this.queryDelay = 10;
22433         }
22434         if(config.minChars === undefined){
22435             this.minChars = 0;
22436         }
22437     }
22438 };
22439
22440 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22441     /**
22442      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22443      */
22444     /**
22445      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22446      * rendering into an Roo.Editor, defaults to false)
22447      */
22448     /**
22449      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22450      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22451      */
22452     /**
22453      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22454      */
22455     /**
22456      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22457      * the dropdown list (defaults to undefined, with no header element)
22458      */
22459
22460      /**
22461      * @cfg {String/Roo.Template} tpl The template to use to render the output
22462      */
22463      
22464     // private
22465     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22466     /**
22467      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22468      */
22469     listWidth: undefined,
22470     /**
22471      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22472      * mode = 'remote' or 'text' if mode = 'local')
22473      */
22474     displayField: undefined,
22475     /**
22476      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22477      * mode = 'remote' or 'value' if mode = 'local'). 
22478      * Note: use of a valueField requires the user make a selection
22479      * in order for a value to be mapped.
22480      */
22481     valueField: undefined,
22482     /**
22483      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22484      * field's data value (defaults to the underlying DOM element's name)
22485      */
22486     hiddenName: undefined,
22487     /**
22488      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22489      */
22490     listClass: '',
22491     /**
22492      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22493      */
22494     selectedClass: 'x-combo-selected',
22495     /**
22496      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22497      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22498      * which displays a downward arrow icon).
22499      */
22500     triggerClass : 'x-form-arrow-trigger',
22501     /**
22502      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22503      */
22504     shadow:'sides',
22505     /**
22506      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22507      * anchor positions (defaults to 'tl-bl')
22508      */
22509     listAlign: 'tl-bl?',
22510     /**
22511      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22512      */
22513     maxHeight: 300,
22514     /**
22515      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22516      * query specified by the allQuery config option (defaults to 'query')
22517      */
22518     triggerAction: 'query',
22519     /**
22520      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22521      * (defaults to 4, does not apply if editable = false)
22522      */
22523     minChars : 4,
22524     /**
22525      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22526      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22527      */
22528     typeAhead: false,
22529     /**
22530      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22531      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22532      */
22533     queryDelay: 500,
22534     /**
22535      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22536      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22537      */
22538     pageSize: 0,
22539     /**
22540      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22541      * when editable = true (defaults to false)
22542      */
22543     selectOnFocus:false,
22544     /**
22545      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22546      */
22547     queryParam: 'query',
22548     /**
22549      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22550      * when mode = 'remote' (defaults to 'Loading...')
22551      */
22552     loadingText: 'Loading...',
22553     /**
22554      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22555      */
22556     resizable: false,
22557     /**
22558      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22559      */
22560     handleHeight : 8,
22561     /**
22562      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22563      * traditional select (defaults to true)
22564      */
22565     editable: true,
22566     /**
22567      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22568      */
22569     allQuery: '',
22570     /**
22571      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22572      */
22573     mode: 'remote',
22574     /**
22575      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22576      * listWidth has a higher value)
22577      */
22578     minListWidth : 70,
22579     /**
22580      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22581      * allow the user to set arbitrary text into the field (defaults to false)
22582      */
22583     forceSelection:false,
22584     /**
22585      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22586      * if typeAhead = true (defaults to 250)
22587      */
22588     typeAheadDelay : 250,
22589     /**
22590      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22591      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22592      */
22593     valueNotFoundText : undefined,
22594     /**
22595      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22596      */
22597     blockFocus : false,
22598     
22599     /**
22600      * @cfg {bool} disableClear Disable showing of clear button.
22601      */
22602     disableClear : false,
22603     
22604     // private
22605     onRender : function(ct, position){
22606         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22607         if(this.hiddenName){
22608             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22609                     'before', true);
22610             this.hiddenField.value =
22611                 this.hiddenValue !== undefined ? this.hiddenValue :
22612                 this.value !== undefined ? this.value : '';
22613
22614             // prevent input submission
22615             this.el.dom.removeAttribute('name');
22616         }
22617         if(Roo.isGecko){
22618             this.el.dom.setAttribute('autocomplete', 'off');
22619         }
22620
22621         var cls = 'x-combo-list';
22622
22623         this.list = new Roo.Layer({
22624             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22625         });
22626
22627         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22628         this.list.setWidth(lw);
22629         this.list.swallowEvent('mousewheel');
22630         this.assetHeight = 0;
22631
22632         if(this.title){
22633             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22634             this.assetHeight += this.header.getHeight();
22635         }
22636
22637         this.innerList = this.list.createChild({cls:cls+'-inner'});
22638         this.innerList.on('mouseover', this.onViewOver, this);
22639         this.innerList.on('mousemove', this.onViewMove, this);
22640         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22641         
22642         if(this.allowBlank && !this.pageSize && !this.disableClear){
22643             this.footer = this.list.createChild({cls:cls+'-ft'});
22644             this.pageTb = new Roo.Toolbar(this.footer);
22645            
22646         }
22647         if(this.pageSize){
22648             this.footer = this.list.createChild({cls:cls+'-ft'});
22649             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22650                     {pageSize: this.pageSize});
22651             
22652         }
22653         
22654         if (this.pageTb && this.allowBlank && !this.disableClear) {
22655             var _this = this;
22656             this.pageTb.add(new Roo.Toolbar.Fill(), {
22657                 cls: 'x-btn-icon x-btn-clear',
22658                 text: '&#160;',
22659                 handler: function()
22660                 {
22661                     _this.collapse();
22662                     _this.clearValue();
22663                     _this.onSelect(false, -1);
22664                 }
22665             });
22666         }
22667         if (this.footer) {
22668             this.assetHeight += this.footer.getHeight();
22669         }
22670         
22671
22672         if(!this.tpl){
22673             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22674         }
22675
22676         this.view = new Roo.View(this.innerList, this.tpl, {
22677             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22678         });
22679
22680         this.view.on('click', this.onViewClick, this);
22681
22682         this.store.on('beforeload', this.onBeforeLoad, this);
22683         this.store.on('load', this.onLoad, this);
22684         this.store.on('loadexception', this.collapse, this);
22685
22686         if(this.resizable){
22687             this.resizer = new Roo.Resizable(this.list,  {
22688                pinned:true, handles:'se'
22689             });
22690             this.resizer.on('resize', function(r, w, h){
22691                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22692                 this.listWidth = w;
22693                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22694                 this.restrictHeight();
22695             }, this);
22696             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22697         }
22698         if(!this.editable){
22699             this.editable = true;
22700             this.setEditable(false);
22701         }
22702     },
22703
22704     // private
22705     initEvents : function(){
22706         Roo.form.ComboBox.superclass.initEvents.call(this);
22707
22708         this.keyNav = new Roo.KeyNav(this.el, {
22709             "up" : function(e){
22710                 this.inKeyMode = true;
22711                 this.selectPrev();
22712             },
22713
22714             "down" : function(e){
22715                 if(!this.isExpanded()){
22716                     this.onTriggerClick();
22717                 }else{
22718                     this.inKeyMode = true;
22719                     this.selectNext();
22720                 }
22721             },
22722
22723             "enter" : function(e){
22724                 this.onViewClick();
22725                 //return true;
22726             },
22727
22728             "esc" : function(e){
22729                 this.collapse();
22730             },
22731
22732             "tab" : function(e){
22733                 this.onViewClick(false);
22734                 return true;
22735             },
22736
22737             scope : this,
22738
22739             doRelay : function(foo, bar, hname){
22740                 if(hname == 'down' || this.scope.isExpanded()){
22741                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22742                 }
22743                 return true;
22744             },
22745
22746             forceKeyDown: true
22747         });
22748         this.queryDelay = Math.max(this.queryDelay || 10,
22749                 this.mode == 'local' ? 10 : 250);
22750         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
22751         if(this.typeAhead){
22752             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
22753         }
22754         if(this.editable !== false){
22755             this.el.on("keyup", this.onKeyUp, this);
22756         }
22757         if(this.forceSelection){
22758             this.on('blur', this.doForce, this);
22759         }
22760     },
22761
22762     onDestroy : function(){
22763         if(this.view){
22764             this.view.setStore(null);
22765             this.view.el.removeAllListeners();
22766             this.view.el.remove();
22767             this.view.purgeListeners();
22768         }
22769         if(this.list){
22770             this.list.destroy();
22771         }
22772         if(this.store){
22773             this.store.un('beforeload', this.onBeforeLoad, this);
22774             this.store.un('load', this.onLoad, this);
22775             this.store.un('loadexception', this.collapse, this);
22776         }
22777         Roo.form.ComboBox.superclass.onDestroy.call(this);
22778     },
22779
22780     // private
22781     fireKey : function(e){
22782         if(e.isNavKeyPress() && !this.list.isVisible()){
22783             this.fireEvent("specialkey", this, e);
22784         }
22785     },
22786
22787     // private
22788     onResize: function(w, h){
22789         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
22790         if(this.list && this.listWidth === undefined){
22791             var lw = Math.max(w, this.minListWidth);
22792             this.list.setWidth(lw);
22793             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22794         }
22795     },
22796
22797     /**
22798      * Allow or prevent the user from directly editing the field text.  If false is passed,
22799      * the user will only be able to select from the items defined in the dropdown list.  This method
22800      * is the runtime equivalent of setting the 'editable' config option at config time.
22801      * @param {Boolean} value True to allow the user to directly edit the field text
22802      */
22803     setEditable : function(value){
22804         if(value == this.editable){
22805             return;
22806         }
22807         this.editable = value;
22808         if(!value){
22809             this.el.dom.setAttribute('readOnly', true);
22810             this.el.on('mousedown', this.onTriggerClick,  this);
22811             this.el.addClass('x-combo-noedit');
22812         }else{
22813             this.el.dom.setAttribute('readOnly', false);
22814             this.el.un('mousedown', this.onTriggerClick,  this);
22815             this.el.removeClass('x-combo-noedit');
22816         }
22817     },
22818
22819     // private
22820     onBeforeLoad : function(){
22821         if(!this.hasFocus){
22822             return;
22823         }
22824         this.innerList.update(this.loadingText ?
22825                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
22826         this.restrictHeight();
22827         this.selectedIndex = -1;
22828     },
22829
22830     // private
22831     onLoad : function(){
22832         if(!this.hasFocus){
22833             return;
22834         }
22835         if(this.store.getCount() > 0){
22836             this.expand();
22837             this.restrictHeight();
22838             if(this.lastQuery == this.allQuery){
22839                 if(this.editable){
22840                     this.el.dom.select();
22841                 }
22842                 if(!this.selectByValue(this.value, true)){
22843                     this.select(0, true);
22844                 }
22845             }else{
22846                 this.selectNext();
22847                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
22848                     this.taTask.delay(this.typeAheadDelay);
22849                 }
22850             }
22851         }else{
22852             this.onEmptyResults();
22853         }
22854         //this.el.focus();
22855     },
22856
22857     // private
22858     onTypeAhead : function(){
22859         if(this.store.getCount() > 0){
22860             var r = this.store.getAt(0);
22861             var newValue = r.data[this.displayField];
22862             var len = newValue.length;
22863             var selStart = this.getRawValue().length;
22864             if(selStart != len){
22865                 this.setRawValue(newValue);
22866                 this.selectText(selStart, newValue.length);
22867             }
22868         }
22869     },
22870
22871     // private
22872     onSelect : function(record, index){
22873         if(this.fireEvent('beforeselect', this, record, index) !== false){
22874             this.setFromData(index > -1 ? record.data : false);
22875             this.collapse();
22876             this.fireEvent('select', this, record, index);
22877         }
22878     },
22879
22880     /**
22881      * Returns the currently selected field value or empty string if no value is set.
22882      * @return {String} value The selected value
22883      */
22884     getValue : function(){
22885         if(this.valueField){
22886             return typeof this.value != 'undefined' ? this.value : '';
22887         }else{
22888             return Roo.form.ComboBox.superclass.getValue.call(this);
22889         }
22890     },
22891
22892     /**
22893      * Clears any text/value currently set in the field
22894      */
22895     clearValue : function(){
22896         if(this.hiddenField){
22897             this.hiddenField.value = '';
22898         }
22899         this.value = '';
22900         this.setRawValue('');
22901         this.lastSelectionText = '';
22902         this.applyEmptyText();
22903     },
22904
22905     /**
22906      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
22907      * will be displayed in the field.  If the value does not match the data value of an existing item,
22908      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
22909      * Otherwise the field will be blank (although the value will still be set).
22910      * @param {String} value The value to match
22911      */
22912     setValue : function(v){
22913         var text = v;
22914         if(this.valueField){
22915             var r = this.findRecord(this.valueField, v);
22916             if(r){
22917                 text = r.data[this.displayField];
22918             }else if(this.valueNotFoundText !== undefined){
22919                 text = this.valueNotFoundText;
22920             }
22921         }
22922         this.lastSelectionText = text;
22923         if(this.hiddenField){
22924             this.hiddenField.value = v;
22925         }
22926         Roo.form.ComboBox.superclass.setValue.call(this, text);
22927         this.value = v;
22928     },
22929     /**
22930      * @property {Object} the last set data for the element
22931      */
22932     
22933     lastData : false,
22934     /**
22935      * Sets the value of the field based on a object which is related to the record format for the store.
22936      * @param {Object} value the value to set as. or false on reset?
22937      */
22938     setFromData : function(o){
22939         var dv = ''; // display value
22940         var vv = ''; // value value..
22941         this.lastData = o;
22942         if (this.displayField) {
22943             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
22944         } else {
22945             // this is an error condition!!!
22946             console.log('no value field set for '+ this.name);
22947         }
22948         
22949         if(this.valueField){
22950             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
22951         }
22952         if(this.hiddenField){
22953             this.hiddenField.value = vv;
22954             
22955             this.lastSelectionText = dv;
22956             Roo.form.ComboBox.superclass.setValue.call(this, dv);
22957             this.value = vv;
22958             return;
22959         }
22960         // no hidden field.. - we store the value in 'value', but still display
22961         // display field!!!!
22962         this.lastSelectionText = dv;
22963         Roo.form.ComboBox.superclass.setValue.call(this, dv);
22964         this.value = vv;
22965         
22966         
22967     },
22968     // private
22969     reset : function(){
22970         // overridden so that last data is reset..
22971         this.setValue(this.originalValue);
22972         this.clearInvalid();
22973         this.lastData = false;
22974     },
22975     // private
22976     findRecord : function(prop, value){
22977         var record;
22978         if(this.store.getCount() > 0){
22979             this.store.each(function(r){
22980                 if(r.data[prop] == value){
22981                     record = r;
22982                     return false;
22983                 }
22984             });
22985         }
22986         return record;
22987     },
22988
22989     // private
22990     onViewMove : function(e, t){
22991         this.inKeyMode = false;
22992     },
22993
22994     // private
22995     onViewOver : function(e, t){
22996         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
22997             return;
22998         }
22999         var item = this.view.findItemFromChild(t);
23000         if(item){
23001             var index = this.view.indexOf(item);
23002             this.select(index, false);
23003         }
23004     },
23005
23006     // private
23007     onViewClick : function(doFocus){
23008         var index = this.view.getSelectedIndexes()[0];
23009         var r = this.store.getAt(index);
23010         if(r){
23011             this.onSelect(r, index);
23012         }
23013         if(doFocus !== false && !this.blockFocus){
23014             this.el.focus();
23015         }
23016     },
23017
23018     // private
23019     restrictHeight : function(){
23020         this.innerList.dom.style.height = '';
23021         var inner = this.innerList.dom;
23022         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23023         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23024         this.list.beginUpdate();
23025         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23026         this.list.alignTo(this.el, this.listAlign);
23027         this.list.endUpdate();
23028     },
23029
23030     // private
23031     onEmptyResults : function(){
23032         this.collapse();
23033     },
23034
23035     /**
23036      * Returns true if the dropdown list is expanded, else false.
23037      */
23038     isExpanded : function(){
23039         return this.list.isVisible();
23040     },
23041
23042     /**
23043      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23044      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23045      * @param {String} value The data value of the item to select
23046      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23047      * selected item if it is not currently in view (defaults to true)
23048      * @return {Boolean} True if the value matched an item in the list, else false
23049      */
23050     selectByValue : function(v, scrollIntoView){
23051         if(v !== undefined && v !== null){
23052             var r = this.findRecord(this.valueField || this.displayField, v);
23053             if(r){
23054                 this.select(this.store.indexOf(r), scrollIntoView);
23055                 return true;
23056             }
23057         }
23058         return false;
23059     },
23060
23061     /**
23062      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23063      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23064      * @param {Number} index The zero-based index of the list item to select
23065      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23066      * selected item if it is not currently in view (defaults to true)
23067      */
23068     select : function(index, scrollIntoView){
23069         this.selectedIndex = index;
23070         this.view.select(index);
23071         if(scrollIntoView !== false){
23072             var el = this.view.getNode(index);
23073             if(el){
23074                 this.innerList.scrollChildIntoView(el, false);
23075             }
23076         }
23077     },
23078
23079     // private
23080     selectNext : function(){
23081         var ct = this.store.getCount();
23082         if(ct > 0){
23083             if(this.selectedIndex == -1){
23084                 this.select(0);
23085             }else if(this.selectedIndex < ct-1){
23086                 this.select(this.selectedIndex+1);
23087             }
23088         }
23089     },
23090
23091     // private
23092     selectPrev : function(){
23093         var ct = this.store.getCount();
23094         if(ct > 0){
23095             if(this.selectedIndex == -1){
23096                 this.select(0);
23097             }else if(this.selectedIndex != 0){
23098                 this.select(this.selectedIndex-1);
23099             }
23100         }
23101     },
23102
23103     // private
23104     onKeyUp : function(e){
23105         if(this.editable !== false && !e.isSpecialKey()){
23106             this.lastKey = e.getKey();
23107             this.dqTask.delay(this.queryDelay);
23108         }
23109     },
23110
23111     // private
23112     validateBlur : function(){
23113         return !this.list || !this.list.isVisible();   
23114     },
23115
23116     // private
23117     initQuery : function(){
23118         this.doQuery(this.getRawValue());
23119     },
23120
23121     // private
23122     doForce : function(){
23123         if(this.el.dom.value.length > 0){
23124             this.el.dom.value =
23125                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23126             this.applyEmptyText();
23127         }
23128     },
23129
23130     /**
23131      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23132      * query allowing the query action to be canceled if needed.
23133      * @param {String} query The SQL query to execute
23134      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23135      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23136      * saved in the current store (defaults to false)
23137      */
23138     doQuery : function(q, forceAll){
23139         if(q === undefined || q === null){
23140             q = '';
23141         }
23142         var qe = {
23143             query: q,
23144             forceAll: forceAll,
23145             combo: this,
23146             cancel:false
23147         };
23148         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23149             return false;
23150         }
23151         q = qe.query;
23152         forceAll = qe.forceAll;
23153         if(forceAll === true || (q.length >= this.minChars)){
23154             if(this.lastQuery != q){
23155                 this.lastQuery = q;
23156                 if(this.mode == 'local'){
23157                     this.selectedIndex = -1;
23158                     if(forceAll){
23159                         this.store.clearFilter();
23160                     }else{
23161                         this.store.filter(this.displayField, q);
23162                     }
23163                     this.onLoad();
23164                 }else{
23165                     this.store.baseParams[this.queryParam] = q;
23166                     this.store.load({
23167                         params: this.getParams(q)
23168                     });
23169                     this.expand();
23170                 }
23171             }else{
23172                 this.selectedIndex = -1;
23173                 this.onLoad();   
23174             }
23175         }
23176     },
23177
23178     // private
23179     getParams : function(q){
23180         var p = {};
23181         //p[this.queryParam] = q;
23182         if(this.pageSize){
23183             p.start = 0;
23184             p.limit = this.pageSize;
23185         }
23186         return p;
23187     },
23188
23189     /**
23190      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23191      */
23192     collapse : function(){
23193         if(!this.isExpanded()){
23194             return;
23195         }
23196         this.list.hide();
23197         Roo.get(document).un('mousedown', this.collapseIf, this);
23198         Roo.get(document).un('mousewheel', this.collapseIf, this);
23199         this.fireEvent('collapse', this);
23200     },
23201
23202     // private
23203     collapseIf : function(e){
23204         if(!e.within(this.wrap) && !e.within(this.list)){
23205             this.collapse();
23206         }
23207     },
23208
23209     /**
23210      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23211      */
23212     expand : function(){
23213         if(this.isExpanded() || !this.hasFocus){
23214             return;
23215         }
23216         this.list.alignTo(this.el, this.listAlign);
23217         this.list.show();
23218         Roo.get(document).on('mousedown', this.collapseIf, this);
23219         Roo.get(document).on('mousewheel', this.collapseIf, this);
23220         this.fireEvent('expand', this);
23221     },
23222
23223     // private
23224     // Implements the default empty TriggerField.onTriggerClick function
23225     onTriggerClick : function(){
23226         if(this.disabled){
23227             return;
23228         }
23229         if(this.isExpanded()){
23230             this.collapse();
23231             if (!this.blockFocus) {
23232                 this.el.focus();
23233             }
23234             
23235         }else {
23236             this.hasFocus = true;
23237             if(this.triggerAction == 'all') {
23238                 this.doQuery(this.allQuery, true);
23239             } else {
23240                 this.doQuery(this.getRawValue());
23241             }
23242             if (!this.blockFocus) {
23243                 this.el.focus();
23244             }
23245         }
23246     }
23247
23248     /** 
23249     * @cfg {Boolean} grow 
23250     * @hide 
23251     */
23252     /** 
23253     * @cfg {Number} growMin 
23254     * @hide 
23255     */
23256     /** 
23257     * @cfg {Number} growMax 
23258     * @hide 
23259     */
23260     /**
23261      * @hide
23262      * @method autoSize
23263      */
23264 });/*
23265  * Based on:
23266  * Ext JS Library 1.1.1
23267  * Copyright(c) 2006-2007, Ext JS, LLC.
23268  *
23269  * Originally Released Under LGPL - original licence link has changed is not relivant.
23270  *
23271  * Fork - LGPL
23272  * <script type="text/javascript">
23273  */
23274 /**
23275  * @class Roo.form.Checkbox
23276  * @extends Roo.form.Field
23277  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23278  * @constructor
23279  * Creates a new Checkbox
23280  * @param {Object} config Configuration options
23281  */
23282 Roo.form.Checkbox = function(config){
23283     Roo.form.Checkbox.superclass.constructor.call(this, config);
23284     this.addEvents({
23285         /**
23286          * @event check
23287          * Fires when the checkbox is checked or unchecked.
23288              * @param {Roo.form.Checkbox} this This checkbox
23289              * @param {Boolean} checked The new checked value
23290              */
23291         check : true
23292     });
23293 };
23294
23295 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23296     /**
23297      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23298      */
23299     focusClass : undefined,
23300     /**
23301      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23302      */
23303     fieldClass: "x-form-field",
23304     /**
23305      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23306      */
23307     checked: false,
23308     /**
23309      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23310      * {tag: "input", type: "checkbox", autocomplete: "off"})
23311      */
23312     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23313     /**
23314      * @cfg {String} boxLabel The text that appears beside the checkbox
23315      */
23316     boxLabel : "",
23317     /**
23318      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23319      */  
23320     inputValue : '1',
23321     /**
23322      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23323      */
23324      valueOff: '0', // value when not checked..
23325
23326     actionMode : 'viewEl', 
23327     //
23328     // private
23329     itemCls : 'x-menu-check-item x-form-item',
23330     groupClass : 'x-menu-group-item',
23331     inputType : 'hidden',
23332     
23333     
23334     inSetChecked: false, // check that we are not calling self...
23335     
23336     inputElement: false, // real input element?
23337     basedOn: false, // ????
23338     
23339     isFormField: true, // not sure where this is needed!!!!
23340
23341     onResize : function(){
23342         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23343         if(!this.boxLabel){
23344             this.el.alignTo(this.wrap, 'c-c');
23345         }
23346     },
23347
23348     initEvents : function(){
23349         Roo.form.Checkbox.superclass.initEvents.call(this);
23350         this.el.on("click", this.onClick,  this);
23351         this.el.on("change", this.onClick,  this);
23352     },
23353
23354
23355     getResizeEl : function(){
23356         return this.wrap;
23357     },
23358
23359     getPositionEl : function(){
23360         return this.wrap;
23361     },
23362
23363     // private
23364     onRender : function(ct, position){
23365         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23366         /*
23367         if(this.inputValue !== undefined){
23368             this.el.dom.value = this.inputValue;
23369         }
23370         */
23371         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23372         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23373         var viewEl = this.wrap.createChild({ 
23374             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23375         this.viewEl = viewEl;   
23376         this.wrap.on('click', this.onClick,  this); 
23377         
23378         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23379         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23380         
23381         
23382         
23383         if(this.boxLabel){
23384             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23385         //    viewEl.on('click', this.onClick,  this); 
23386         }
23387         //if(this.checked){
23388             this.setChecked(this.checked);
23389         //}else{
23390             //this.checked = this.el.dom;
23391         //}
23392
23393     },
23394
23395     // private
23396     initValue : Roo.emptyFn,
23397
23398     /**
23399      * Returns the checked state of the checkbox.
23400      * @return {Boolean} True if checked, else false
23401      */
23402     getValue : function(){
23403         if(this.el){
23404             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23405         }
23406         return this.valueOff;
23407         
23408     },
23409
23410         // private
23411     onClick : function(){ 
23412         this.setChecked(!this.checked);
23413
23414         //if(this.el.dom.checked != this.checked){
23415         //    this.setValue(this.el.dom.checked);
23416        // }
23417     },
23418
23419     /**
23420      * Sets the checked state of the checkbox.
23421      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
23422      */
23423     setValue : function(v,suppressEvent){
23424         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23425         //if(this.el && this.el.dom){
23426         //    this.el.dom.checked = this.checked;
23427         //    this.el.dom.defaultChecked = this.checked;
23428         //}
23429         this.setChecked(v === this.inputValue);
23430         //this.fireEvent("check", this, this.checked);
23431     },
23432     // private..
23433     setChecked : function(state,suppressEvent)
23434     {
23435         if (this.inSetChecked) {
23436             this.checked = state;
23437             return;
23438         }
23439         
23440     
23441         if(this.wrap){
23442             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23443         }
23444         this.checked = state;
23445         if(suppressEvent !== true){
23446             this.fireEvent('checkchange', this, state);
23447         }
23448         this.inSetChecked = true;
23449         this.el.dom.value = state ? this.inputValue : this.valueOff;
23450         this.inSetChecked = false;
23451         
23452     },
23453     // handle setting of hidden value by some other method!!?!?
23454     setFromHidden: function()
23455     {
23456         if(!this.el){
23457             return;
23458         }
23459         //console.log("SET FROM HIDDEN");
23460         //alert('setFrom hidden');
23461         this.setValue(this.el.dom.value);
23462     },
23463     
23464     onDestroy : function()
23465     {
23466         if(this.viewEl){
23467             Roo.get(this.viewEl).remove();
23468         }
23469          
23470         Roo.form.Checkbox.superclass.onDestroy.call(this);
23471     }
23472
23473 });/*
23474  * Based on:
23475  * Ext JS Library 1.1.1
23476  * Copyright(c) 2006-2007, Ext JS, LLC.
23477  *
23478  * Originally Released Under LGPL - original licence link has changed is not relivant.
23479  *
23480  * Fork - LGPL
23481  * <script type="text/javascript">
23482  */
23483  
23484 /**
23485  * @class Roo.form.Radio
23486  * @extends Roo.form.Checkbox
23487  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23488  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23489  * @constructor
23490  * Creates a new Radio
23491  * @param {Object} config Configuration options
23492  */
23493 Roo.form.Radio = function(){
23494     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23495 };
23496 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23497     inputType: 'radio',
23498
23499     /**
23500      * If this radio is part of a group, it will return the selected value
23501      * @return {String}
23502      */
23503     getGroupValue : function(){
23504         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23505     }
23506 });//<script type="text/javascript">
23507
23508 /*
23509  * Ext JS Library 1.1.1
23510  * Copyright(c) 2006-2007, Ext JS, LLC.
23511  * licensing@extjs.com
23512  * 
23513  * http://www.extjs.com/license
23514  */
23515  
23516  /*
23517   * 
23518   * Known bugs:
23519   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23520   * - IE ? - no idea how much works there.
23521   * 
23522   * 
23523   * 
23524   */
23525  
23526
23527 /**
23528  * @class Ext.form.HtmlEditor
23529  * @extends Ext.form.Field
23530  * Provides a lightweight HTML Editor component.
23531  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23532  * 
23533  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23534  * supported by this editor.</b><br/><br/>
23535  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23536  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23537  */
23538 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23539       /**
23540      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23541      */
23542     toolbars : false,
23543     /**
23544      * @cfg {String} createLinkText The default text for the create link prompt
23545      */
23546     createLinkText : 'Please enter the URL for the link:',
23547     /**
23548      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23549      */
23550     defaultLinkValue : 'http:/'+'/',
23551    
23552     
23553     // id of frame..
23554     frameId: false,
23555     
23556     // private properties
23557     validationEvent : false,
23558     deferHeight: true,
23559     initialized : false,
23560     activated : false,
23561     sourceEditMode : false,
23562     onFocus : Roo.emptyFn,
23563     iframePad:3,
23564     hideMode:'offsets',
23565     defaultAutoCreate : {
23566         tag: "textarea",
23567         style:"width:500px;height:300px;",
23568         autocomplete: "off"
23569     },
23570
23571     // private
23572     initComponent : function(){
23573         this.addEvents({
23574             /**
23575              * @event initialize
23576              * Fires when the editor is fully initialized (including the iframe)
23577              * @param {HtmlEditor} this
23578              */
23579             initialize: true,
23580             /**
23581              * @event activate
23582              * Fires when the editor is first receives the focus. Any insertion must wait
23583              * until after this event.
23584              * @param {HtmlEditor} this
23585              */
23586             activate: true,
23587              /**
23588              * @event beforesync
23589              * Fires before the textarea is updated with content from the editor iframe. Return false
23590              * to cancel the sync.
23591              * @param {HtmlEditor} this
23592              * @param {String} html
23593              */
23594             beforesync: true,
23595              /**
23596              * @event beforepush
23597              * Fires before the iframe editor is updated with content from the textarea. Return false
23598              * to cancel the push.
23599              * @param {HtmlEditor} this
23600              * @param {String} html
23601              */
23602             beforepush: true,
23603              /**
23604              * @event sync
23605              * Fires when the textarea is updated with content from the editor iframe.
23606              * @param {HtmlEditor} this
23607              * @param {String} html
23608              */
23609             sync: true,
23610              /**
23611              * @event push
23612              * Fires when the iframe editor is updated with content from the textarea.
23613              * @param {HtmlEditor} this
23614              * @param {String} html
23615              */
23616             push: true,
23617              /**
23618              * @event editmodechange
23619              * Fires when the editor switches edit modes
23620              * @param {HtmlEditor} this
23621              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23622              */
23623             editmodechange: true,
23624             /**
23625              * @event editorevent
23626              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23627              * @param {HtmlEditor} this
23628              */
23629             editorevent: true
23630         })
23631     },
23632
23633     /**
23634      * Protected method that will not generally be called directly. It
23635      * is called when the editor creates its toolbar. Override this method if you need to
23636      * add custom toolbar buttons.
23637      * @param {HtmlEditor} editor
23638      */
23639     createToolbar : function(editor){
23640         if (!editor.toolbars || !editor.toolbars.length) {
23641             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23642         }
23643         
23644         for (var i =0 ; i < editor.toolbars.length;i++) {
23645             editor.toolbars[i].init(editor);
23646         }
23647          
23648         
23649     },
23650
23651     /**
23652      * Protected method that will not generally be called directly. It
23653      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23654      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23655      */
23656     getDocMarkup : function(){
23657         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23658     },
23659
23660     // private
23661     onRender : function(ct, position){
23662         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23663         this.el.dom.style.border = '0 none';
23664         this.el.dom.setAttribute('tabIndex', -1);
23665         this.el.addClass('x-hidden');
23666         if(Roo.isIE){ // fix IE 1px bogus margin
23667             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23668         }
23669         this.wrap = this.el.wrap({
23670             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23671         });
23672
23673         this.frameId = Roo.id();
23674         this.createToolbar(this);
23675         
23676         
23677         
23678         
23679       
23680         
23681         var iframe = this.wrap.createChild({
23682             tag: 'iframe',
23683             id: this.frameId,
23684             name: this.frameId,
23685             frameBorder : 'no',
23686             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23687         });
23688         
23689        // console.log(iframe);
23690         //this.wrap.dom.appendChild(iframe);
23691
23692         this.iframe = iframe.dom;
23693
23694          this.assignDocWin();
23695         
23696         this.doc.designMode = 'on';
23697        
23698         this.doc.open();
23699         this.doc.write(this.getDocMarkup());
23700         this.doc.close();
23701
23702         
23703         var task = { // must defer to wait for browser to be ready
23704             run : function(){
23705                 //console.log("run task?" + this.doc.readyState);
23706                 this.assignDocWin();
23707                 if(this.doc.body || this.doc.readyState == 'complete'){
23708                     try {
23709                         
23710                        
23711                         this.doc.designMode="on";
23712                     } catch (e) {
23713                         return;
23714                     }
23715                     Roo.TaskMgr.stop(task);
23716                     this.initEditor.defer(10, this);
23717                 }
23718             },
23719             interval : 10,
23720             duration:10000,
23721             scope: this
23722         };
23723         Roo.TaskMgr.start(task);
23724
23725         if(!this.width){
23726             this.setSize(this.el.getSize());
23727         }
23728     },
23729
23730     // private
23731     onResize : function(w, h){
23732         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23733         if(this.el && this.iframe){
23734             if(typeof w == 'number'){
23735                 var aw = w - this.wrap.getFrameWidth('lr');
23736                 this.el.setWidth(this.adjustWidth('textarea', aw));
23737                 this.iframe.style.width = aw + 'px';
23738             }
23739             if(typeof h == 'number'){
23740                 var tbh = 0;
23741                 for (var i =0; i < this.toolbars.length;i++) {
23742                     // fixme - ask toolbars for heights?
23743                     tbh += this.toolbars[i].tb.el.getHeight();
23744                 }
23745                 
23746                 
23747                 
23748                 
23749                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23750                 this.el.setHeight(this.adjustWidth('textarea', ah));
23751                 this.iframe.style.height = ah + 'px';
23752                 if(this.doc){
23753                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
23754                 }
23755             }
23756         }
23757     },
23758
23759     /**
23760      * Toggles the editor between standard and source edit mode.
23761      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23762      */
23763     toggleSourceEdit : function(sourceEditMode){
23764         
23765         this.sourceEditMode = sourceEditMode === true;
23766         
23767         if(this.sourceEditMode){
23768           
23769             this.syncValue();
23770             this.iframe.className = 'x-hidden';
23771             this.el.removeClass('x-hidden');
23772             this.el.dom.removeAttribute('tabIndex');
23773             this.el.focus();
23774         }else{
23775              
23776             this.pushValue();
23777             this.iframe.className = '';
23778             this.el.addClass('x-hidden');
23779             this.el.dom.setAttribute('tabIndex', -1);
23780             this.deferFocus();
23781         }
23782         this.setSize(this.wrap.getSize());
23783         this.fireEvent('editmodechange', this, this.sourceEditMode);
23784     },
23785
23786     // private used internally
23787     createLink : function(){
23788         var url = prompt(this.createLinkText, this.defaultLinkValue);
23789         if(url && url != 'http:/'+'/'){
23790             this.relayCmd('createlink', url);
23791         }
23792     },
23793
23794     // private (for BoxComponent)
23795     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23796
23797     // private (for BoxComponent)
23798     getResizeEl : function(){
23799         return this.wrap;
23800     },
23801
23802     // private (for BoxComponent)
23803     getPositionEl : function(){
23804         return this.wrap;
23805     },
23806
23807     // private
23808     initEvents : function(){
23809         this.originalValue = this.getValue();
23810     },
23811
23812     /**
23813      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23814      * @method
23815      */
23816     markInvalid : Roo.emptyFn,
23817     /**
23818      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23819      * @method
23820      */
23821     clearInvalid : Roo.emptyFn,
23822
23823     setValue : function(v){
23824         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23825         this.pushValue();
23826     },
23827
23828     /**
23829      * Protected method that will not generally be called directly. If you need/want
23830      * custom HTML cleanup, this is the method you should override.
23831      * @param {String} html The HTML to be cleaned
23832      * return {String} The cleaned HTML
23833      */
23834     cleanHtml : function(html){
23835         html = String(html);
23836         if(html.length > 5){
23837             if(Roo.isSafari){ // strip safari nonsense
23838                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23839             }
23840         }
23841         if(html == '&nbsp;'){
23842             html = '';
23843         }
23844         return html;
23845     },
23846
23847     /**
23848      * Protected method that will not generally be called directly. Syncs the contents
23849      * of the editor iframe with the textarea.
23850      */
23851     syncValue : function(){
23852         if(this.initialized){
23853             var bd = (this.doc.body || this.doc.documentElement);
23854             var html = bd.innerHTML;
23855             if(Roo.isSafari){
23856                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23857                 var m = bs.match(/text-align:(.*?);/i);
23858                 if(m && m[1]){
23859                     html = '<div style="'+m[0]+'">' + html + '</div>';
23860                 }
23861             }
23862             html = this.cleanHtml(html);
23863             if(this.fireEvent('beforesync', this, html) !== false){
23864                 this.el.dom.value = html;
23865                 this.fireEvent('sync', this, html);
23866             }
23867         }
23868     },
23869
23870     /**
23871      * Protected method that will not generally be called directly. Pushes the value of the textarea
23872      * into the iframe editor.
23873      */
23874     pushValue : function(){
23875         if(this.initialized){
23876             var v = this.el.dom.value;
23877             if(v.length < 1){
23878                 v = '&#160;';
23879             }
23880             if(this.fireEvent('beforepush', this, v) !== false){
23881                 (this.doc.body || this.doc.documentElement).innerHTML = v;
23882                 this.fireEvent('push', this, v);
23883             }
23884         }
23885     },
23886
23887     // private
23888     deferFocus : function(){
23889         this.focus.defer(10, this);
23890     },
23891
23892     // doc'ed in Field
23893     focus : function(){
23894         if(this.win && !this.sourceEditMode){
23895             this.win.focus();
23896         }else{
23897             this.el.focus();
23898         }
23899     },
23900     
23901     assignDocWin: function()
23902     {
23903         var iframe = this.iframe;
23904         
23905          if(Roo.isIE){
23906             this.doc = iframe.contentWindow.document;
23907             this.win = iframe.contentWindow;
23908         } else {
23909             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23910             this.win = Roo.get(this.frameId).dom.contentWindow;
23911         }
23912     },
23913     
23914     // private
23915     initEditor : function(){
23916         //console.log("INIT EDITOR");
23917         this.assignDocWin();
23918         
23919         
23920         
23921         this.doc.designMode="on";
23922         this.doc.open();
23923         this.doc.write(this.getDocMarkup());
23924         this.doc.close();
23925         
23926         var dbody = (this.doc.body || this.doc.documentElement);
23927         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23928         // this copies styles from the containing element into thsi one..
23929         // not sure why we need all of this..
23930         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23931         ss['background-attachment'] = 'fixed'; // w3c
23932         dbody.bgProperties = 'fixed'; // ie
23933         Roo.DomHelper.applyStyles(dbody, ss);
23934         Roo.EventManager.on(this.doc, {
23935             'mousedown': this.onEditorEvent,
23936             'dblclick': this.onEditorEvent,
23937             'click': this.onEditorEvent,
23938             'keyup': this.onEditorEvent,
23939             buffer:100,
23940             scope: this
23941         });
23942         if(Roo.isGecko){
23943             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
23944         }
23945         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23946             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23947         }
23948         this.initialized = true;
23949
23950         this.fireEvent('initialize', this);
23951         this.pushValue();
23952     },
23953
23954     // private
23955     onDestroy : function(){
23956         
23957         
23958         
23959         if(this.rendered){
23960             
23961             for (var i =0; i < this.toolbars.length;i++) {
23962                 // fixme - ask toolbars for heights?
23963                 this.toolbars[i].onDestroy();
23964             }
23965             
23966             this.wrap.dom.innerHTML = '';
23967             this.wrap.remove();
23968         }
23969     },
23970
23971     // private
23972     onFirstFocus : function(){
23973         
23974         this.assignDocWin();
23975         
23976         
23977         this.activated = true;
23978         for (var i =0; i < this.toolbars.length;i++) {
23979             this.toolbars[i].onFirstFocus();
23980         }
23981        
23982         if(Roo.isGecko){ // prevent silly gecko errors
23983             this.win.focus();
23984             var s = this.win.getSelection();
23985             if(!s.focusNode || s.focusNode.nodeType != 3){
23986                 var r = s.getRangeAt(0);
23987                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23988                 r.collapse(true);
23989                 this.deferFocus();
23990             }
23991             try{
23992                 this.execCmd('useCSS', true);
23993                 this.execCmd('styleWithCSS', false);
23994             }catch(e){}
23995         }
23996         this.fireEvent('activate', this);
23997     },
23998
23999     // private
24000     adjustFont: function(btn){
24001         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24002         //if(Roo.isSafari){ // safari
24003         //    adjust *= 2;
24004        // }
24005         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24006         if(Roo.isSafari){ // safari
24007             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24008             v =  (v < 10) ? 10 : v;
24009             v =  (v > 48) ? 48 : v;
24010             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24011             
24012         }
24013         
24014         
24015         v = Math.max(1, v+adjust);
24016         
24017         this.execCmd('FontSize', v  );
24018     },
24019
24020     onEditorEvent : function(e){
24021         this.fireEvent('editorevent', this, e);
24022       //  this.updateToolbar();
24023         this.syncValue();
24024     },
24025
24026     insertTag : function(tg)
24027     {
24028         // could be a bit smarter... -> wrap the current selected tRoo..
24029         
24030         this.execCmd("formatblock",   tg);
24031         
24032     },
24033     
24034     insertText : function(txt)
24035     {
24036         
24037         
24038         range = this.createRange();
24039         range.deleteContents();
24040                //alert(Sender.getAttribute('label'));
24041                
24042         range.insertNode(this.doc.createTextNode(txt));
24043     } ,
24044     
24045     // private
24046     relayBtnCmd : function(btn){
24047         this.relayCmd(btn.cmd);
24048     },
24049
24050     /**
24051      * Executes a Midas editor command on the editor document and performs necessary focus and
24052      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24053      * @param {String} cmd The Midas command
24054      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24055      */
24056     relayCmd : function(cmd, value){
24057         this.win.focus();
24058         this.execCmd(cmd, value);
24059         this.fireEvent('editorevent', this);
24060         //this.updateToolbar();
24061         this.deferFocus();
24062     },
24063
24064     /**
24065      * Executes a Midas editor command directly on the editor document.
24066      * For visual commands, you should use {@link #relayCmd} instead.
24067      * <b>This should only be called after the editor is initialized.</b>
24068      * @param {String} cmd The Midas command
24069      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24070      */
24071     execCmd : function(cmd, value){
24072         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24073         this.syncValue();
24074     },
24075
24076     // private
24077     applyCommand : function(e){
24078         if(e.ctrlKey){
24079             var c = e.getCharCode(), cmd;
24080             if(c > 0){
24081                 c = String.fromCharCode(c);
24082                 switch(c){
24083                     case 'b':
24084                         cmd = 'bold';
24085                     break;
24086                     case 'i':
24087                         cmd = 'italic';
24088                     break;
24089                     case 'u':
24090                         cmd = 'underline';
24091                     break;
24092                 }
24093                 if(cmd){
24094                     this.win.focus();
24095                     this.execCmd(cmd);
24096                     this.deferFocus();
24097                     e.preventDefault();
24098                 }
24099             }
24100         }
24101     },
24102
24103     /**
24104      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24105      * to insert tRoo.
24106      * @param {String} text
24107      */
24108     insertAtCursor : function(text){
24109         if(!this.activated){
24110             return;
24111         }
24112         if(Roo.isIE){
24113             this.win.focus();
24114             var r = this.doc.selection.createRange();
24115             if(r){
24116                 r.collapse(true);
24117                 r.pasteHTML(text);
24118                 this.syncValue();
24119                 this.deferFocus();
24120             }
24121         }else if(Roo.isGecko || Roo.isOpera){
24122             this.win.focus();
24123             this.execCmd('InsertHTML', text);
24124             this.deferFocus();
24125         }else if(Roo.isSafari){
24126             this.execCmd('InsertText', text);
24127             this.deferFocus();
24128         }
24129     },
24130
24131     // private
24132     fixKeys : function(){ // load time branching for fastest keydown performance
24133         if(Roo.isIE){
24134             return function(e){
24135                 var k = e.getKey(), r;
24136                 if(k == e.TAB){
24137                     e.stopEvent();
24138                     r = this.doc.selection.createRange();
24139                     if(r){
24140                         r.collapse(true);
24141                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24142                         this.deferFocus();
24143                     }
24144                 }else if(k == e.ENTER){
24145                     r = this.doc.selection.createRange();
24146                     if(r){
24147                         var target = r.parentElement();
24148                         if(!target || target.tagName.toLowerCase() != 'li'){
24149                             e.stopEvent();
24150                             r.pasteHTML('<br />');
24151                             r.collapse(false);
24152                             r.select();
24153                         }
24154                     }
24155                 }
24156             };
24157         }else if(Roo.isOpera){
24158             return function(e){
24159                 var k = e.getKey();
24160                 if(k == e.TAB){
24161                     e.stopEvent();
24162                     this.win.focus();
24163                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24164                     this.deferFocus();
24165                 }
24166             };
24167         }else if(Roo.isSafari){
24168             return function(e){
24169                 var k = e.getKey();
24170                 if(k == e.TAB){
24171                     e.stopEvent();
24172                     this.execCmd('InsertText','\t');
24173                     this.deferFocus();
24174                 }
24175              };
24176         }
24177     }(),
24178     
24179     getAllAncestors: function()
24180     {
24181         var p = this.getSelectedNode();
24182         var a = [];
24183         if (!p) {
24184             a.push(p); // push blank onto stack..
24185             p = this.getParentElement();
24186         }
24187         
24188         
24189         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24190             a.push(p);
24191             p = p.parentNode;
24192         }
24193         a.push(this.doc.body);
24194         return a;
24195     },
24196     lastSel : false,
24197     lastSelNode : false,
24198     
24199     
24200     getSelection : function() 
24201     {
24202         this.assignDocWin();
24203         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24204     },
24205     
24206     getSelectedNode: function() 
24207     {
24208         // this may only work on Gecko!!!
24209         
24210         // should we cache this!!!!
24211         
24212         
24213         
24214          
24215         var range = this.createRange(this.getSelection());
24216         
24217         if (Roo.isIE) {
24218             var parent = range.parentElement();
24219             while (true) {
24220                 var testRange = range.duplicate();
24221                 testRange.moveToElementText(parent);
24222                 if (testRange.inRange(range)) {
24223                     break;
24224                 }
24225                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24226                     break;
24227                 }
24228                 parent = parent.parentElement;
24229             }
24230             return parent;
24231         }
24232         
24233         
24234         var ar = range.endContainer.childNodes;
24235         if (!ar.length) {
24236             ar = range.commonAncestorContainer.childNodes;
24237             //alert(ar.length);
24238         }
24239         var nodes = [];
24240         var other_nodes = [];
24241         var has_other_nodes = false;
24242         for (var i=0;i<ar.length;i++) {
24243             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24244                 continue;
24245             }
24246             // fullly contained node.
24247             
24248             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24249                 nodes.push(ar[i]);
24250                 continue;
24251             }
24252             
24253             // probably selected..
24254             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24255                 other_nodes.push(ar[i]);
24256                 continue;
24257             }
24258             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24259                 continue;
24260             }
24261             
24262             
24263             has_other_nodes = true;
24264         }
24265         if (!nodes.length && other_nodes.length) {
24266             nodes= other_nodes;
24267         }
24268         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24269             return false;
24270         }
24271         
24272         return nodes[0];
24273     },
24274     createRange: function(sel)
24275     {
24276         // this has strange effects when using with 
24277         // top toolbar - not sure if it's a great idea.
24278         //this.editor.contentWindow.focus();
24279         if (typeof sel != "undefined") {
24280             try {
24281                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24282             } catch(e) {
24283                 return this.doc.createRange();
24284             }
24285         } else {
24286             return this.doc.createRange();
24287         }
24288     },
24289     getParentElement: function()
24290     {
24291         
24292         this.assignDocWin();
24293         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24294         
24295         var range = this.createRange(sel);
24296          
24297         try {
24298             var p = range.commonAncestorContainer;
24299             while (p.nodeType == 3) { // text node
24300                 p = p.parentNode;
24301             }
24302             return p;
24303         } catch (e) {
24304             return null;
24305         }
24306     
24307     },
24308     
24309     
24310     
24311     // BC Hacks - cause I cant work out what i was trying to do..
24312     rangeIntersectsNode : function(range, node)
24313     {
24314         var nodeRange = node.ownerDocument.createRange();
24315         try {
24316             nodeRange.selectNode(node);
24317         }
24318         catch (e) {
24319             nodeRange.selectNodeContents(node);
24320         }
24321
24322         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24323                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24324     },
24325     rangeCompareNode : function(range, node) {
24326         var nodeRange = node.ownerDocument.createRange();
24327         try {
24328             nodeRange.selectNode(node);
24329         } catch (e) {
24330             nodeRange.selectNodeContents(node);
24331         }
24332         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24333         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24334
24335         if (nodeIsBefore && !nodeIsAfter)
24336             return 0;
24337         if (!nodeIsBefore && nodeIsAfter)
24338             return 1;
24339         if (nodeIsBefore && nodeIsAfter)
24340             return 2;
24341
24342         return 3;
24343     }
24344
24345     
24346     
24347     // hide stuff that is not compatible
24348     /**
24349      * @event blur
24350      * @hide
24351      */
24352     /**
24353      * @event change
24354      * @hide
24355      */
24356     /**
24357      * @event focus
24358      * @hide
24359      */
24360     /**
24361      * @event specialkey
24362      * @hide
24363      */
24364     /**
24365      * @cfg {String} fieldClass @hide
24366      */
24367     /**
24368      * @cfg {String} focusClass @hide
24369      */
24370     /**
24371      * @cfg {String} autoCreate @hide
24372      */
24373     /**
24374      * @cfg {String} inputType @hide
24375      */
24376     /**
24377      * @cfg {String} invalidClass @hide
24378      */
24379     /**
24380      * @cfg {String} invalidText @hide
24381      */
24382     /**
24383      * @cfg {String} msgFx @hide
24384      */
24385     /**
24386      * @cfg {String} validateOnBlur @hide
24387      */
24388 });// <script type="text/javascript">
24389 /*
24390  * Based on
24391  * Ext JS Library 1.1.1
24392  * Copyright(c) 2006-2007, Ext JS, LLC.
24393  *  
24394  
24395  */
24396
24397 /**
24398  * @class Roo.form.HtmlEditorToolbar1
24399  * Basic Toolbar
24400  * 
24401  * Usage:
24402  *
24403  new Roo.form.HtmlEditor({
24404     ....
24405     toolbars : [
24406         new Roo.form.HtmlEditorToolbar1({
24407             disable : { fonts: 1 , format: 1, ..., ... , ...],
24408             btns : [ .... ]
24409         })
24410     }
24411      
24412  * 
24413  * @cfg {Object} disable List of elements to disable..
24414  * @cfg {Array} btns List of additional buttons.
24415  * 
24416  * 
24417  * NEEDS Extra CSS? 
24418  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24419  */
24420  
24421 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24422 {
24423     
24424     Roo.apply(this, config);
24425     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24426     // dont call parent... till later.
24427 }
24428
24429 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24430     
24431     tb: false,
24432     
24433     rendered: false,
24434     
24435     editor : false,
24436     /**
24437      * @cfg {Object} disable  List of toolbar elements to disable
24438          
24439      */
24440     disable : false,
24441       /**
24442      * @cfg {Array} fontFamilies An array of available font families
24443      */
24444     fontFamilies : [
24445         'Arial',
24446         'Courier New',
24447         'Tahoma',
24448         'Times New Roman',
24449         'Verdana'
24450     ],
24451     
24452     specialChars : [
24453            "&#169;",
24454           "&#174;",     
24455           "&#8482;",    
24456           "&#163;" ,    
24457          // "&#8212;",    
24458           "&#8230;",    
24459           "&#247;" ,    
24460         //  "&#225;" ,     ?? a acute?
24461            "&#8364;"    , //Euro
24462        //   "&#8220;"    ,
24463         //  "&#8221;"    ,
24464         //  "&#8226;"    ,
24465           "&#176;"  //   , // degrees
24466
24467          // "&#233;"     , // e ecute
24468          // "&#250;"     , // u ecute?
24469     ],
24470     inputElements : [ 
24471             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24472             "input:submit", "input:button", "select", "textarea", "label" ],
24473     formats : [
24474         ["p"] ,  
24475         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24476         ["pre"],[ "code"], 
24477         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24478     ],
24479      /**
24480      * @cfg {String} defaultFont default font to use.
24481      */
24482     defaultFont: 'tahoma',
24483    
24484     fontSelect : false,
24485     
24486     
24487     formatCombo : false,
24488     
24489     init : function(editor)
24490     {
24491         this.editor = editor;
24492         
24493         
24494         var fid = editor.frameId;
24495         var etb = this;
24496         function btn(id, toggle, handler){
24497             var xid = fid + '-'+ id ;
24498             return {
24499                 id : xid,
24500                 cmd : id,
24501                 cls : 'x-btn-icon x-edit-'+id,
24502                 enableToggle:toggle !== false,
24503                 scope: editor, // was editor...
24504                 handler:handler||editor.relayBtnCmd,
24505                 clickEvent:'mousedown',
24506                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24507                 tabIndex:-1
24508             };
24509         }
24510         
24511         
24512         
24513         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24514         this.tb = tb;
24515          // stop form submits
24516         tb.el.on('click', function(e){
24517             e.preventDefault(); // what does this do?
24518         });
24519
24520         if(!this.disable.font && !Roo.isSafari){
24521             /* why no safari for fonts
24522             editor.fontSelect = tb.el.createChild({
24523                 tag:'select',
24524                 tabIndex: -1,
24525                 cls:'x-font-select',
24526                 html: editor.createFontOptions()
24527             });
24528             editor.fontSelect.on('change', function(){
24529                 var font = editor.fontSelect.dom.value;
24530                 editor.relayCmd('fontname', font);
24531                 editor.deferFocus();
24532             }, editor);
24533             tb.add(
24534                 editor.fontSelect.dom,
24535                 '-'
24536             );
24537             */
24538         };
24539         if(!this.disable.formats){
24540             this.formatCombo = new Roo.form.ComboBox({
24541                 store: new Roo.data.SimpleStore({
24542                     id : 'tag',
24543                     fields: ['tag'],
24544                     data : this.formats // from states.js
24545                 }),
24546                 blockFocus : true,
24547                 //autoCreate : {tag: "div",  size: "20"},
24548                 displayField:'tag',
24549                 typeAhead: false,
24550                 mode: 'local',
24551                 editable : false,
24552                 triggerAction: 'all',
24553                 emptyText:'Add tag',
24554                 selectOnFocus:true,
24555                 width:135,
24556                 listeners : {
24557                     'select': function(c, r, i) {
24558                         editor.insertTag(r.get('tag'));
24559                         editor.focus();
24560                     }
24561                 }
24562
24563             });
24564             tb.addField(this.formatCombo);
24565             
24566         }
24567         
24568         if(!this.disable.format){
24569             tb.add(
24570                 btn('bold'),
24571                 btn('italic'),
24572                 btn('underline')
24573             );
24574         };
24575         if(!this.disable.fontSize){
24576             tb.add(
24577                 '-',
24578                 
24579                 
24580                 btn('increasefontsize', false, editor.adjustFont),
24581                 btn('decreasefontsize', false, editor.adjustFont)
24582             );
24583         };
24584         
24585         
24586         if(this.disable.colors){
24587             tb.add(
24588                 '-', {
24589                     id:editor.frameId +'-forecolor',
24590                     cls:'x-btn-icon x-edit-forecolor',
24591                     clickEvent:'mousedown',
24592                     tooltip: this.buttonTips['forecolor'] || undefined,
24593                     tabIndex:-1,
24594                     menu : new Roo.menu.ColorMenu({
24595                         allowReselect: true,
24596                         focus: Roo.emptyFn,
24597                         value:'000000',
24598                         plain:true,
24599                         selectHandler: function(cp, color){
24600                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24601                             editor.deferFocus();
24602                         },
24603                         scope: editor,
24604                         clickEvent:'mousedown'
24605                     })
24606                 }, {
24607                     id:editor.frameId +'backcolor',
24608                     cls:'x-btn-icon x-edit-backcolor',
24609                     clickEvent:'mousedown',
24610                     tooltip: this.buttonTips['backcolor'] || undefined,
24611                     tabIndex:-1,
24612                     menu : new Roo.menu.ColorMenu({
24613                         focus: Roo.emptyFn,
24614                         value:'FFFFFF',
24615                         plain:true,
24616                         allowReselect: true,
24617                         selectHandler: function(cp, color){
24618                             if(Roo.isGecko){
24619                                 editor.execCmd('useCSS', false);
24620                                 editor.execCmd('hilitecolor', color);
24621                                 editor.execCmd('useCSS', true);
24622                                 editor.deferFocus();
24623                             }else{
24624                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
24625                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
24626                                 editor.deferFocus();
24627                             }
24628                         },
24629                         scope:editor,
24630                         clickEvent:'mousedown'
24631                     })
24632                 }
24633             );
24634         };
24635         // now add all the items...
24636         
24637
24638         if(!this.disable.alignments){
24639             tb.add(
24640                 '-',
24641                 btn('justifyleft'),
24642                 btn('justifycenter'),
24643                 btn('justifyright')
24644             );
24645         };
24646
24647         //if(!Roo.isSafari){
24648             if(!this.disable.links){
24649                 tb.add(
24650                     '-',
24651                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
24652                 );
24653             };
24654
24655             if(!this.disable.lists){
24656                 tb.add(
24657                     '-',
24658                     btn('insertorderedlist'),
24659                     btn('insertunorderedlist')
24660                 );
24661             }
24662             if(!this.disable.sourceEdit){
24663                 tb.add(
24664                     '-',
24665                     btn('sourceedit', true, function(btn){
24666                         this.toggleSourceEdit(btn.pressed);
24667                     })
24668                 );
24669             }
24670         //}
24671         
24672         var smenu = { };
24673         // special menu.. - needs to be tidied up..
24674         if (!this.disable.special) {
24675             smenu = {
24676                 text: "&#169;",
24677                 cls: 'x-edit-none',
24678                 menu : {
24679                     items : []
24680                    }
24681             };
24682             for (var i =0; i < this.specialChars.length; i++) {
24683                 smenu.menu.items.push({
24684                     
24685                     text: this.specialChars[i],
24686                     handler: function(a,b) {
24687                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
24688                     },
24689                     tabIndex:-1
24690                 });
24691             }
24692             
24693             
24694             tb.add(smenu);
24695             
24696             
24697         }
24698         if (this.btns) {
24699             for(var i =0; i< this.btns.length;i++) {
24700                 var b = this.btns[i];
24701                 b.cls =  'x-edit-none';
24702                 b.scope = editor;
24703                 tb.add(b);
24704             }
24705         
24706         }
24707         
24708         
24709         
24710         // disable everything...
24711         
24712         this.tb.items.each(function(item){
24713            if(item.id != editor.frameId+ '-sourceedit'){
24714                 item.disable();
24715             }
24716         });
24717         this.rendered = true;
24718         
24719         // the all the btns;
24720         editor.on('editorevent', this.updateToolbar, this);
24721         // other toolbars need to implement this..
24722         //editor.on('editmodechange', this.updateToolbar, this);
24723     },
24724     
24725     
24726     
24727     /**
24728      * Protected method that will not generally be called directly. It triggers
24729      * a toolbar update by reading the markup state of the current selection in the editor.
24730      */
24731     updateToolbar: function(){
24732
24733         if(!this.editor.activated){
24734             this.editor.onFirstFocus();
24735             return;
24736         }
24737
24738         var btns = this.tb.items.map, 
24739             doc = this.editor.doc,
24740             frameId = this.editor.frameId;
24741
24742         if(!this.disable.font && !Roo.isSafari){
24743             /*
24744             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
24745             if(name != this.fontSelect.dom.value){
24746                 this.fontSelect.dom.value = name;
24747             }
24748             */
24749         }
24750         if(!this.disable.format){
24751             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
24752             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
24753             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
24754         }
24755         if(!this.disable.alignments){
24756             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
24757             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
24758             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
24759         }
24760         if(!Roo.isSafari && !this.disable.lists){
24761             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
24762             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
24763         }
24764         
24765         var ans = this.editor.getAllAncestors();
24766         if (this.formatCombo) {
24767             
24768             
24769             var store = this.formatCombo.store;
24770             this.formatCombo.setValue("");
24771             for (var i =0; i < ans.length;i++) {
24772                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
24773                     // select it..
24774                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24775                     break;
24776                 }
24777             }
24778         }
24779         
24780         
24781         
24782         // hides menus... - so this cant be on a menu...
24783         Roo.menu.MenuMgr.hideAll();
24784
24785         //this.editorsyncValue();
24786     },
24787    
24788     
24789     createFontOptions : function(){
24790         var buf = [], fs = this.fontFamilies, ff, lc;
24791         for(var i = 0, len = fs.length; i< len; i++){
24792             ff = fs[i];
24793             lc = ff.toLowerCase();
24794             buf.push(
24795                 '<option value="',lc,'" style="font-family:',ff,';"',
24796                     (this.defaultFont == lc ? ' selected="true">' : '>'),
24797                     ff,
24798                 '</option>'
24799             );
24800         }
24801         return buf.join('');
24802     },
24803     
24804     toggleSourceEdit : function(sourceEditMode){
24805         if(sourceEditMode === undefined){
24806             sourceEditMode = !this.sourceEditMode;
24807         }
24808         this.sourceEditMode = sourceEditMode === true;
24809         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
24810         // just toggle the button?
24811         if(btn.pressed !== this.editor.sourceEditMode){
24812             btn.toggle(this.editor.sourceEditMode);
24813             return;
24814         }
24815         
24816         if(this.sourceEditMode){
24817             this.tb.items.each(function(item){
24818                 if(item.cmd != 'sourceedit'){
24819                     item.disable();
24820                 }
24821             });
24822           
24823         }else{
24824             if(this.initialized){
24825                 this.tb.items.each(function(item){
24826                     item.enable();
24827                 });
24828             }
24829             
24830         }
24831         // tell the editor that it's been pressed..
24832         this.editor.toggleSourceEdit(sourceEditMode);
24833        
24834     },
24835      /**
24836      * Object collection of toolbar tooltips for the buttons in the editor. The key
24837      * is the command id associated with that button and the value is a valid QuickTips object.
24838      * For example:
24839 <pre><code>
24840 {
24841     bold : {
24842         title: 'Bold (Ctrl+B)',
24843         text: 'Make the selected text bold.',
24844         cls: 'x-html-editor-tip'
24845     },
24846     italic : {
24847         title: 'Italic (Ctrl+I)',
24848         text: 'Make the selected text italic.',
24849         cls: 'x-html-editor-tip'
24850     },
24851     ...
24852 </code></pre>
24853     * @type Object
24854      */
24855     buttonTips : {
24856         bold : {
24857             title: 'Bold (Ctrl+B)',
24858             text: 'Make the selected text bold.',
24859             cls: 'x-html-editor-tip'
24860         },
24861         italic : {
24862             title: 'Italic (Ctrl+I)',
24863             text: 'Make the selected text italic.',
24864             cls: 'x-html-editor-tip'
24865         },
24866         underline : {
24867             title: 'Underline (Ctrl+U)',
24868             text: 'Underline the selected text.',
24869             cls: 'x-html-editor-tip'
24870         },
24871         increasefontsize : {
24872             title: 'Grow Text',
24873             text: 'Increase the font size.',
24874             cls: 'x-html-editor-tip'
24875         },
24876         decreasefontsize : {
24877             title: 'Shrink Text',
24878             text: 'Decrease the font size.',
24879             cls: 'x-html-editor-tip'
24880         },
24881         backcolor : {
24882             title: 'Text Highlight Color',
24883             text: 'Change the background color of the selected text.',
24884             cls: 'x-html-editor-tip'
24885         },
24886         forecolor : {
24887             title: 'Font Color',
24888             text: 'Change the color of the selected text.',
24889             cls: 'x-html-editor-tip'
24890         },
24891         justifyleft : {
24892             title: 'Align Text Left',
24893             text: 'Align text to the left.',
24894             cls: 'x-html-editor-tip'
24895         },
24896         justifycenter : {
24897             title: 'Center Text',
24898             text: 'Center text in the editor.',
24899             cls: 'x-html-editor-tip'
24900         },
24901         justifyright : {
24902             title: 'Align Text Right',
24903             text: 'Align text to the right.',
24904             cls: 'x-html-editor-tip'
24905         },
24906         insertunorderedlist : {
24907             title: 'Bullet List',
24908             text: 'Start a bulleted list.',
24909             cls: 'x-html-editor-tip'
24910         },
24911         insertorderedlist : {
24912             title: 'Numbered List',
24913             text: 'Start a numbered list.',
24914             cls: 'x-html-editor-tip'
24915         },
24916         createlink : {
24917             title: 'Hyperlink',
24918             text: 'Make the selected text a hyperlink.',
24919             cls: 'x-html-editor-tip'
24920         },
24921         sourceedit : {
24922             title: 'Source Edit',
24923             text: 'Switch to source editing mode.',
24924             cls: 'x-html-editor-tip'
24925         }
24926     },
24927     // private
24928     onDestroy : function(){
24929         if(this.rendered){
24930             
24931             this.tb.items.each(function(item){
24932                 if(item.menu){
24933                     item.menu.removeAll();
24934                     if(item.menu.el){
24935                         item.menu.el.destroy();
24936                     }
24937                 }
24938                 item.destroy();
24939             });
24940              
24941         }
24942     },
24943     onFirstFocus: function() {
24944         this.tb.items.each(function(item){
24945            item.enable();
24946         });
24947     }
24948 });
24949
24950
24951
24952
24953 // <script type="text/javascript">
24954 /*
24955  * Based on
24956  * Ext JS Library 1.1.1
24957  * Copyright(c) 2006-2007, Ext JS, LLC.
24958  *  
24959  
24960  */
24961
24962  
24963 /**
24964  * @class Roo.form.HtmlEditor.ToolbarContext
24965  * Context Toolbar
24966  * 
24967  * Usage:
24968  *
24969  new Roo.form.HtmlEditor({
24970     ....
24971     toolbars : [
24972         new Roo.form.HtmlEditor.ToolbarStandard(),
24973         new Roo.form.HtmlEditor.ToolbarContext()
24974         })
24975     }
24976      
24977  * 
24978  * @config : {Object} disable List of elements to disable.. (not done yet.)
24979  * 
24980  * 
24981  */
24982
24983 Roo.form.HtmlEditor.ToolbarContext = function(config)
24984 {
24985     
24986     Roo.apply(this, config);
24987     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24988     // dont call parent... till later.
24989 }
24990 Roo.form.HtmlEditor.ToolbarContext.types = {
24991     'IMG' : {
24992         width : {
24993             title: "Width",
24994             width: 40
24995         },
24996         height:  {
24997             title: "Height",
24998             width: 40
24999         },
25000         align: {
25001             title: "Align",
25002             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25003             width : 80
25004             
25005         },
25006         border: {
25007             title: "Border",
25008             width: 40
25009         },
25010         alt: {
25011             title: "Alt",
25012             width: 120
25013         },
25014         src : {
25015             title: "Src",
25016             width: 220
25017         }
25018         
25019     },
25020     'A' : {
25021         name : {
25022             title: "Name",
25023             width: 50
25024         },
25025         href:  {
25026             title: "Href",
25027             width: 220
25028         } // border?
25029         
25030     },
25031     'TABLE' : {
25032         rows : {
25033             title: "Rows",
25034             width: 20
25035         },
25036         cols : {
25037             title: "Cols",
25038             width: 20
25039         },
25040         width : {
25041             title: "Width",
25042             width: 40
25043         },
25044         height : {
25045             title: "Height",
25046             width: 40
25047         },
25048         border : {
25049             title: "Border",
25050             width: 20
25051         }
25052     },
25053     'TD' : {
25054         width : {
25055             title: "Width",
25056             width: 40
25057         },
25058         height : {
25059             title: "Height",
25060             width: 40
25061         },   
25062         align: {
25063             title: "Align",
25064             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25065             width: 40
25066         },
25067         valign: {
25068             title: "Valign",
25069             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25070             width: 40
25071         },
25072         colspan: {
25073             title: "Colspan",
25074             width: 20
25075             
25076         }
25077     },
25078     'INPUT' : {
25079         name : {
25080             title: "name",
25081             width: 120
25082         },
25083         value : {
25084             title: "Value",
25085             width: 120
25086         },
25087         width : {
25088             title: "Width",
25089             width: 40
25090         }
25091     },
25092     'LABEL' : {
25093         'for' : {
25094             title: "For",
25095             width: 120
25096         }
25097     },
25098     'TEXTAREA' : {
25099           name : {
25100             title: "name",
25101             width: 120
25102         },
25103         rows : {
25104             title: "Rows",
25105             width: 20
25106         },
25107         cols : {
25108             title: "Cols",
25109             width: 20
25110         }
25111     },
25112     'SELECT' : {
25113         name : {
25114             title: "name",
25115             width: 120
25116         },
25117         selectoptions : {
25118             title: "Options",
25119             width: 200
25120         }
25121     },
25122     'BODY' : {
25123         title : {
25124             title: "title",
25125             width: 120,
25126             disabled : true
25127         }
25128     }
25129 };
25130
25131
25132
25133 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25134     
25135     tb: false,
25136     
25137     rendered: false,
25138     
25139     editor : false,
25140     /**
25141      * @cfg {Object} disable  List of toolbar elements to disable
25142          
25143      */
25144     disable : false,
25145     
25146     
25147     
25148     toolbars : false,
25149     
25150     init : function(editor)
25151     {
25152         this.editor = editor;
25153         
25154         
25155         var fid = editor.frameId;
25156         var etb = this;
25157         function btn(id, toggle, handler){
25158             var xid = fid + '-'+ id ;
25159             return {
25160                 id : xid,
25161                 cmd : id,
25162                 cls : 'x-btn-icon x-edit-'+id,
25163                 enableToggle:toggle !== false,
25164                 scope: editor, // was editor...
25165                 handler:handler||editor.relayBtnCmd,
25166                 clickEvent:'mousedown',
25167                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25168                 tabIndex:-1
25169             };
25170         }
25171         // create a new element.
25172         var wdiv = editor.wrap.createChild({
25173                 tag: 'div'
25174             }, editor.wrap.dom.firstChild.nextSibling, true);
25175         
25176         // can we do this more than once??
25177         
25178          // stop form submits
25179       
25180  
25181         // disable everything...
25182         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25183         this.toolbars = {};
25184            
25185         for (var i in  ty) {
25186             this.toolbars[i] = this.buildToolbar(ty[i],i);
25187         }
25188         this.tb = this.toolbars.BODY;
25189         this.tb.el.show();
25190         
25191          
25192         this.rendered = true;
25193         
25194         // the all the btns;
25195         editor.on('editorevent', this.updateToolbar, this);
25196         // other toolbars need to implement this..
25197         //editor.on('editmodechange', this.updateToolbar, this);
25198     },
25199     
25200     
25201     
25202     /**
25203      * Protected method that will not generally be called directly. It triggers
25204      * a toolbar update by reading the markup state of the current selection in the editor.
25205      */
25206     updateToolbar: function(){
25207
25208         if(!this.editor.activated){
25209             this.editor.onFirstFocus();
25210             return;
25211         }
25212
25213         
25214         var ans = this.editor.getAllAncestors();
25215         
25216         // pick
25217         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25218         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25219         sel = sel ? sel : this.editor.doc.body;
25220         sel = sel.tagName.length ? sel : this.editor.doc.body;
25221         var tn = sel.tagName.toUpperCase();
25222         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25223         tn = sel.tagName.toUpperCase();
25224         if (this.tb.name  == tn) {
25225             return; // no change
25226         }
25227         this.tb.el.hide();
25228         ///console.log("show: " + tn);
25229         this.tb =  this.toolbars[tn];
25230         this.tb.el.show();
25231         this.tb.fields.each(function(e) {
25232             e.setValue(sel.getAttribute(e.name));
25233         });
25234         this.tb.selectedNode = sel;
25235         
25236         
25237         Roo.menu.MenuMgr.hideAll();
25238
25239         //this.editorsyncValue();
25240     },
25241    
25242        
25243     // private
25244     onDestroy : function(){
25245         if(this.rendered){
25246             
25247             this.tb.items.each(function(item){
25248                 if(item.menu){
25249                     item.menu.removeAll();
25250                     if(item.menu.el){
25251                         item.menu.el.destroy();
25252                     }
25253                 }
25254                 item.destroy();
25255             });
25256              
25257         }
25258     },
25259     onFirstFocus: function() {
25260         // need to do this for all the toolbars..
25261         this.tb.items.each(function(item){
25262            item.enable();
25263         });
25264     },
25265     buildToolbar: function(tlist, nm)
25266     {
25267         var editor = this.editor;
25268          // create a new element.
25269         var wdiv = editor.wrap.createChild({
25270                 tag: 'div'
25271             }, editor.wrap.dom.firstChild.nextSibling, true);
25272         
25273        
25274         var tb = new Roo.Toolbar(wdiv);
25275         tb.add(nm+ ":&nbsp;");
25276         for (var i in tlist) {
25277             var item = tlist[i];
25278             tb.add(item.title + ":&nbsp;");
25279             if (item.opts) {
25280                 // fixme
25281                 
25282               
25283                 tb.addField( new Roo.form.ComboBox({
25284                     store: new Roo.data.SimpleStore({
25285                         id : 'val',
25286                         fields: ['val'],
25287                         data : item.opts // from states.js
25288                     }),
25289                     name : i,
25290                     displayField:'val',
25291                     typeAhead: false,
25292                     mode: 'local',
25293                     editable : false,
25294                     triggerAction: 'all',
25295                     emptyText:'Select',
25296                     selectOnFocus:true,
25297                     width: item.width ? item.width  : 130,
25298                     listeners : {
25299                         'select': function(c, r, i) {
25300                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25301                         }
25302                     }
25303
25304                 }));
25305                 continue;
25306                     
25307                 
25308                 
25309                 
25310                 
25311                 tb.addField( new Roo.form.TextField({
25312                     name: i,
25313                     width: 100,
25314                     //allowBlank:false,
25315                     value: ''
25316                 }));
25317                 continue;
25318             }
25319             tb.addField( new Roo.form.TextField({
25320                 name: i,
25321                 width: item.width,
25322                 //allowBlank:true,
25323                 value: '',
25324                 listeners: {
25325                     'change' : function(f, nv, ov) {
25326                         tb.selectedNode.setAttribute(f.name, nv);
25327                     }
25328                 }
25329             }));
25330              
25331         }
25332         tb.el.on('click', function(e){
25333             e.preventDefault(); // what does this do?
25334         });
25335         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25336         tb.el.hide();
25337         tb.name = nm;
25338         // dont need to disable them... as they will get hidden
25339         return tb;
25340          
25341         
25342     }
25343     
25344     
25345     
25346     
25347 });
25348
25349
25350
25351
25352
25353 /*
25354  * Based on:
25355  * Ext JS Library 1.1.1
25356  * Copyright(c) 2006-2007, Ext JS, LLC.
25357  *
25358  * Originally Released Under LGPL - original licence link has changed is not relivant.
25359  *
25360  * Fork - LGPL
25361  * <script type="text/javascript">
25362  */
25363  
25364 /**
25365  * @class Roo.form.BasicForm
25366  * @extends Roo.util.Observable
25367  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25368  * @constructor
25369  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25370  * @param {Object} config Configuration options
25371  */
25372 Roo.form.BasicForm = function(el, config){
25373     this.allItems = [];
25374     this.childForms = [];
25375     Roo.apply(this, config);
25376     /*
25377      * The Roo.form.Field items in this form.
25378      * @type MixedCollection
25379      */
25380      
25381      
25382     this.items = new Roo.util.MixedCollection(false, function(o){
25383         return o.id || (o.id = Roo.id());
25384     });
25385     this.addEvents({
25386         /**
25387          * @event beforeaction
25388          * Fires before any action is performed. Return false to cancel the action.
25389          * @param {Form} this
25390          * @param {Action} action The action to be performed
25391          */
25392         beforeaction: true,
25393         /**
25394          * @event actionfailed
25395          * Fires when an action fails.
25396          * @param {Form} this
25397          * @param {Action} action The action that failed
25398          */
25399         actionfailed : true,
25400         /**
25401          * @event actioncomplete
25402          * Fires when an action is completed.
25403          * @param {Form} this
25404          * @param {Action} action The action that completed
25405          */
25406         actioncomplete : true
25407     });
25408     if(el){
25409         this.initEl(el);
25410     }
25411     Roo.form.BasicForm.superclass.constructor.call(this);
25412 };
25413
25414 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25415     /**
25416      * @cfg {String} method
25417      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25418      */
25419     /**
25420      * @cfg {DataReader} reader
25421      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25422      * This is optional as there is built-in support for processing JSON.
25423      */
25424     /**
25425      * @cfg {DataReader} errorReader
25426      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25427      * This is completely optional as there is built-in support for processing JSON.
25428      */
25429     /**
25430      * @cfg {String} url
25431      * The URL to use for form actions if one isn't supplied in the action options.
25432      */
25433     /**
25434      * @cfg {Boolean} fileUpload
25435      * Set to true if this form is a file upload.
25436      */
25437     /**
25438      * @cfg {Object} baseParams
25439      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25440      */
25441     /**
25442      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25443      */
25444     timeout: 30,
25445
25446     // private
25447     activeAction : null,
25448
25449     /**
25450      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25451      * or setValues() data instead of when the form was first created.
25452      */
25453     trackResetOnLoad : false,
25454     
25455     
25456     /**
25457      * childForms - used for multi-tab forms
25458      * @type {Array}
25459      */
25460     childForms : false,
25461     
25462     /**
25463      * allItems - full list of fields.
25464      * @type {Array}
25465      */
25466     allItems : false,
25467     
25468     /**
25469      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
25470      * element by passing it or its id or mask the form itself by passing in true.
25471      * @type Mixed
25472      */
25473     waitMsgTarget : undefined,
25474
25475     // private
25476     initEl : function(el){
25477         this.el = Roo.get(el);
25478         this.id = this.el.id || Roo.id();
25479         this.el.on('submit', this.onSubmit, this);
25480         this.el.addClass('x-form');
25481     },
25482
25483     // private
25484     onSubmit : function(e){
25485         e.stopEvent();
25486     },
25487
25488     /**
25489      * Returns true if client-side validation on the form is successful.
25490      * @return Boolean
25491      */
25492     isValid : function(){
25493         var valid = true;
25494         this.items.each(function(f){
25495            if(!f.validate()){
25496                valid = false;
25497            }
25498         });
25499         return valid;
25500     },
25501
25502     /**
25503      * Returns true if any fields in this form have changed since their original load.
25504      * @return Boolean
25505      */
25506     isDirty : function(){
25507         var dirty = false;
25508         this.items.each(function(f){
25509            if(f.isDirty()){
25510                dirty = true;
25511                return false;
25512            }
25513         });
25514         return dirty;
25515     },
25516
25517     /**
25518      * Performs a predefined action (submit or load) or custom actions you define on this form.
25519      * @param {String} actionName The name of the action type
25520      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25521      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25522      * accept other config options):
25523      * <pre>
25524 Property          Type             Description
25525 ----------------  ---------------  ----------------------------------------------------------------------------------
25526 url               String           The url for the action (defaults to the form's url)
25527 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25528 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25529 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25530                                    validate the form on the client (defaults to false)
25531      * </pre>
25532      * @return {BasicForm} this
25533      */
25534     doAction : function(action, options){
25535         if(typeof action == 'string'){
25536             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25537         }
25538         if(this.fireEvent('beforeaction', this, action) !== false){
25539             this.beforeAction(action);
25540             action.run.defer(100, action);
25541         }
25542         return this;
25543     },
25544
25545     /**
25546      * Shortcut to do a submit action.
25547      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25548      * @return {BasicForm} this
25549      */
25550     submit : function(options){
25551         this.doAction('submit', options);
25552         return this;
25553     },
25554
25555     /**
25556      * Shortcut to do a load action.
25557      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25558      * @return {BasicForm} this
25559      */
25560     load : function(options){
25561         this.doAction('load', options);
25562         return this;
25563     },
25564
25565     /**
25566      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25567      * @param {Record} record The record to edit
25568      * @return {BasicForm} this
25569      */
25570     updateRecord : function(record){
25571         record.beginEdit();
25572         var fs = record.fields;
25573         fs.each(function(f){
25574             var field = this.findField(f.name);
25575             if(field){
25576                 record.set(f.name, field.getValue());
25577             }
25578         }, this);
25579         record.endEdit();
25580         return this;
25581     },
25582
25583     /**
25584      * Loads an Roo.data.Record into this form.
25585      * @param {Record} record The record to load
25586      * @return {BasicForm} this
25587      */
25588     loadRecord : function(record){
25589         this.setValues(record.data);
25590         return this;
25591     },
25592
25593     // private
25594     beforeAction : function(action){
25595         var o = action.options;
25596         if(o.waitMsg){
25597             if(this.waitMsgTarget === true){
25598                 this.el.mask(o.waitMsg, 'x-mask-loading');
25599             }else if(this.waitMsgTarget){
25600                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25601                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
25602             }else{
25603                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
25604             }
25605         }
25606     },
25607
25608     // private
25609     afterAction : function(action, success){
25610         this.activeAction = null;
25611         var o = action.options;
25612         if(o.waitMsg){
25613             if(this.waitMsgTarget === true){
25614                 this.el.unmask();
25615             }else if(this.waitMsgTarget){
25616                 this.waitMsgTarget.unmask();
25617             }else{
25618                 Roo.MessageBox.updateProgress(1);
25619                 Roo.MessageBox.hide();
25620             }
25621         }
25622         if(success){
25623             if(o.reset){
25624                 this.reset();
25625             }
25626             Roo.callback(o.success, o.scope, [this, action]);
25627             this.fireEvent('actioncomplete', this, action);
25628         }else{
25629             Roo.callback(o.failure, o.scope, [this, action]);
25630             this.fireEvent('actionfailed', this, action);
25631         }
25632     },
25633
25634     /**
25635      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25636      * @param {String} id The value to search for
25637      * @return Field
25638      */
25639     findField : function(id){
25640         var field = this.items.get(id);
25641         if(!field){
25642             this.items.each(function(f){
25643                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25644                     field = f;
25645                     return false;
25646                 }
25647             });
25648         }
25649         return field || null;
25650     },
25651
25652     /**
25653      * Add a secondary form to this one, 
25654      * Used to provide tabbed forms. One form is primary, with hidden values 
25655      * which mirror the elements from the other forms.
25656      * 
25657      * @param {Roo.form.Form} form to add.
25658      * 
25659      */
25660     addForm : function(form){
25661        
25662         this.childForms.push(form);
25663         Roo.each(form.allItems, function (fe) {
25664             
25665             if (this.findField(fe.name)) { // already added..
25666                 return;
25667             }
25668             this.add( new Roo.form.Hidden({
25669                 name : fe.name
25670             }));
25671         }, this);
25672         
25673     },
25674     /**
25675      * Mark fields in this form invalid in bulk.
25676      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25677      * @return {BasicForm} this
25678      */
25679     markInvalid : function(errors){
25680         if(errors instanceof Array){
25681             for(var i = 0, len = errors.length; i < len; i++){
25682                 var fieldError = errors[i];
25683                 var f = this.findField(fieldError.id);
25684                 if(f){
25685                     f.markInvalid(fieldError.msg);
25686                 }
25687             }
25688         }else{
25689             var field, id;
25690             for(id in errors){
25691                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25692                     field.markInvalid(errors[id]);
25693                 }
25694             }
25695         }
25696         Roo.each(this.childForms || [], function (f) {
25697             f.markInvalid(errors);
25698         });
25699         
25700         return this;
25701     },
25702
25703     /**
25704      * Set values for fields in this form in bulk.
25705      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25706      * @return {BasicForm} this
25707      */
25708     setValues : function(values){
25709         if(values instanceof Array){ // array of objects
25710             for(var i = 0, len = values.length; i < len; i++){
25711                 var v = values[i];
25712                 var f = this.findField(v.id);
25713                 if(f){
25714                     f.setValue(v.value);
25715                     if(this.trackResetOnLoad){
25716                         f.originalValue = f.getValue();
25717                     }
25718                 }
25719             }
25720         }else{ // object hash
25721             var field, id;
25722             for(id in values){
25723                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25724                     
25725                     if (field.setFromData && 
25726                         field.valueField && 
25727                         field.displayField &&
25728                         // combos' with local stores can 
25729                         // be queried via setValue()
25730                         // to set their value..
25731                         (field.store && !field.store.isLocal)
25732                         ) {
25733                         // it's a combo
25734                         var sd = { };
25735                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25736                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25737                         field.setFromData(sd);
25738                         
25739                     } else {
25740                         field.setValue(values[id]);
25741                     }
25742                     
25743                     
25744                     if(this.trackResetOnLoad){
25745                         field.originalValue = field.getValue();
25746                     }
25747                 }
25748             }
25749         }
25750          
25751         Roo.each(this.childForms || [], function (f) {
25752             f.setValues(values);
25753         });
25754                 
25755         return this;
25756     },
25757
25758     /**
25759      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25760      * they are returned as an array.
25761      * @param {Boolean} asString
25762      * @return {Object}
25763      */
25764     getValues : function(asString){
25765         if (this.childForms) {
25766             // copy values from the child forms
25767             Roo.each(this.childForms, function (f) {
25768                 if (f.allFields) {
25769                     Roo.each(f.allFields, function (e) {
25770                         if (e.name && e.getValue && this.findField(e.name)) {
25771                             this.findField(e.name).setValue(e.getValue());
25772                         }
25773                     });
25774                 }
25775             }, this);
25776         }
25777         
25778         
25779         
25780         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25781         if(asString === true){
25782             return fs;
25783         }
25784         return Roo.urlDecode(fs);
25785     },
25786
25787     /**
25788      * Clears all invalid messages in this form.
25789      * @return {BasicForm} this
25790      */
25791     clearInvalid : function(){
25792         this.items.each(function(f){
25793            f.clearInvalid();
25794         });
25795         
25796         Roo.each(this.childForms || [], function (f) {
25797             f.clearInvalid();
25798         });
25799         
25800         
25801         return this;
25802     },
25803
25804     /**
25805      * Resets this form.
25806      * @return {BasicForm} this
25807      */
25808     reset : function(){
25809         this.items.each(function(f){
25810             f.reset();
25811         });
25812         
25813         Roo.each(this.childForms || [], function (f) {
25814             f.reset();
25815         });
25816        
25817         
25818         return this;
25819     },
25820
25821     /**
25822      * Add Roo.form components to this form.
25823      * @param {Field} field1
25824      * @param {Field} field2 (optional)
25825      * @param {Field} etc (optional)
25826      * @return {BasicForm} this
25827      */
25828     add : function(){
25829         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25830         return this;
25831     },
25832
25833
25834     /**
25835      * Removes a field from the items collection (does NOT remove its markup).
25836      * @param {Field} field
25837      * @return {BasicForm} this
25838      */
25839     remove : function(field){
25840         this.items.remove(field);
25841         return this;
25842     },
25843
25844     /**
25845      * Looks at the fields in this form, checks them for an id attribute,
25846      * and calls applyTo on the existing dom element with that id.
25847      * @return {BasicForm} this
25848      */
25849     render : function(){
25850         this.items.each(function(f){
25851             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25852                 f.applyTo(f.id);
25853             }
25854         });
25855         return this;
25856     },
25857
25858     /**
25859      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25860      * @param {Object} values
25861      * @return {BasicForm} this
25862      */
25863     applyToFields : function(o){
25864         this.items.each(function(f){
25865            Roo.apply(f, o);
25866         });
25867         return this;
25868     },
25869
25870     /**
25871      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25872      * @param {Object} values
25873      * @return {BasicForm} this
25874      */
25875     applyIfToFields : function(o){
25876         this.items.each(function(f){
25877            Roo.applyIf(f, o);
25878         });
25879         return this;
25880     }
25881 });
25882
25883 // back compat
25884 Roo.BasicForm = Roo.form.BasicForm;/*
25885  * Based on:
25886  * Ext JS Library 1.1.1
25887  * Copyright(c) 2006-2007, Ext JS, LLC.
25888  *
25889  * Originally Released Under LGPL - original licence link has changed is not relivant.
25890  *
25891  * Fork - LGPL
25892  * <script type="text/javascript">
25893  */
25894
25895 /**
25896  * @class Roo.form.Form
25897  * @extends Roo.form.BasicForm
25898  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25899  * @constructor
25900  * @param {Object} config Configuration options
25901  */
25902 Roo.form.Form = function(config){
25903     var xitems =  [];
25904     if (config.items) {
25905         xitems = config.items;
25906         delete config.items;
25907     }
25908    
25909     
25910     Roo.form.Form.superclass.constructor.call(this, null, config);
25911     this.url = this.url || this.action;
25912     if(!this.root){
25913         this.root = new Roo.form.Layout(Roo.applyIf({
25914             id: Roo.id()
25915         }, config));
25916     }
25917     this.active = this.root;
25918     /**
25919      * Array of all the buttons that have been added to this form via {@link addButton}
25920      * @type Array
25921      */
25922     this.buttons = [];
25923     this.allItems = [];
25924     this.addEvents({
25925         /**
25926          * @event clientvalidation
25927          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25928          * @param {Form} this
25929          * @param {Boolean} valid true if the form has passed client-side validation
25930          */
25931         clientvalidation: true,
25932         /**
25933          * @event rendered
25934          * Fires when the form is rendered
25935          * @param {Roo.form.Form} form
25936          */
25937         rendered : true
25938     });
25939     
25940     Roo.each(xitems, this.addxtype, this);
25941     
25942     
25943     
25944 };
25945
25946 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25947     /**
25948      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25949      */
25950     /**
25951      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25952      */
25953     /**
25954      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25955      */
25956     buttonAlign:'center',
25957
25958     /**
25959      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25960      */
25961     minButtonWidth:75,
25962
25963     /**
25964      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25965      * This property cascades to child containers if not set.
25966      */
25967     labelAlign:'left',
25968
25969     /**
25970      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25971      * fires a looping event with that state. This is required to bind buttons to the valid
25972      * state using the config value formBind:true on the button.
25973      */
25974     monitorValid : false,
25975
25976     /**
25977      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25978      */
25979     monitorPoll : 200,
25980
25981   
25982     /**
25983      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25984      * fields are added and the column is closed. If no fields are passed the column remains open
25985      * until end() is called.
25986      * @param {Object} config The config to pass to the column
25987      * @param {Field} field1 (optional)
25988      * @param {Field} field2 (optional)
25989      * @param {Field} etc (optional)
25990      * @return Column The column container object
25991      */
25992     column : function(c){
25993         var col = new Roo.form.Column(c);
25994         this.start(col);
25995         if(arguments.length > 1){ // duplicate code required because of Opera
25996             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25997             this.end();
25998         }
25999         return col;
26000     },
26001
26002     /**
26003      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26004      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26005      * until end() is called.
26006      * @param {Object} config The config to pass to the fieldset
26007      * @param {Field} field1 (optional)
26008      * @param {Field} field2 (optional)
26009      * @param {Field} etc (optional)
26010      * @return FieldSet The fieldset container object
26011      */
26012     fieldset : function(c){
26013         var fs = new Roo.form.FieldSet(c);
26014         this.start(fs);
26015         if(arguments.length > 1){ // duplicate code required because of Opera
26016             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26017             this.end();
26018         }
26019         return fs;
26020     },
26021
26022     /**
26023      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26024      * fields are added and the container is closed. If no fields are passed the container remains open
26025      * until end() is called.
26026      * @param {Object} config The config to pass to the Layout
26027      * @param {Field} field1 (optional)
26028      * @param {Field} field2 (optional)
26029      * @param {Field} etc (optional)
26030      * @return Layout The container object
26031      */
26032     container : function(c){
26033         var l = new Roo.form.Layout(c);
26034         this.start(l);
26035         if(arguments.length > 1){ // duplicate code required because of Opera
26036             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26037             this.end();
26038         }
26039         return l;
26040     },
26041
26042     /**
26043      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26044      * @param {Object} container A Roo.form.Layout or subclass of Layout
26045      * @return {Form} this
26046      */
26047     start : function(c){
26048         // cascade label info
26049         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26050         this.active.stack.push(c);
26051         c.ownerCt = this.active;
26052         this.active = c;
26053         return this;
26054     },
26055
26056     /**
26057      * Closes the current open container
26058      * @return {Form} this
26059      */
26060     end : function(){
26061         if(this.active == this.root){
26062             return this;
26063         }
26064         this.active = this.active.ownerCt;
26065         return this;
26066     },
26067
26068     /**
26069      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26070      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26071      * as the label of the field.
26072      * @param {Field} field1
26073      * @param {Field} field2 (optional)
26074      * @param {Field} etc. (optional)
26075      * @return {Form} this
26076      */
26077     add : function(){
26078         this.active.stack.push.apply(this.active.stack, arguments);
26079         this.allItems.push.apply(this.allItems,arguments);
26080         var r = [];
26081         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26082             if(a[i].isFormField){
26083                 r.push(a[i]);
26084             }
26085         }
26086         if(r.length > 0){
26087             Roo.form.Form.superclass.add.apply(this, r);
26088         }
26089         return this;
26090     },
26091     
26092
26093     
26094     
26095     
26096      /**
26097      * Find any element that has been added to a form, using it's ID or name
26098      * This can include framesets, columns etc. along with regular fields..
26099      * @param {String} id - id or name to find.
26100      
26101      * @return {Element} e - or false if nothing found.
26102      */
26103     findbyId : function(id)
26104     {
26105         var ret = false;
26106         if (!id) {
26107             return ret;
26108         }
26109         Ext.each(this.allItems, function(f){
26110             if (f.id == id || f.name == id ){
26111                 ret = f;
26112                 return false;
26113             }
26114         });
26115         return ret;
26116     },
26117
26118     
26119     
26120     /**
26121      * Render this form into the passed container. This should only be called once!
26122      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26123      * @return {Form} this
26124      */
26125     render : function(ct){
26126         ct = Roo.get(ct);
26127         var o = this.autoCreate || {
26128             tag: 'form',
26129             method : this.method || 'POST',
26130             id : this.id || Roo.id()
26131         };
26132         this.initEl(ct.createChild(o));
26133
26134         this.root.render(this.el);
26135
26136         this.items.each(function(f){
26137             f.render('x-form-el-'+f.id);
26138         });
26139
26140         if(this.buttons.length > 0){
26141             // tables are required to maintain order and for correct IE layout
26142             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26143                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26144                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26145             }}, null, true);
26146             var tr = tb.getElementsByTagName('tr')[0];
26147             for(var i = 0, len = this.buttons.length; i < len; i++) {
26148                 var b = this.buttons[i];
26149                 var td = document.createElement('td');
26150                 td.className = 'x-form-btn-td';
26151                 b.render(tr.appendChild(td));
26152             }
26153         }
26154         if(this.monitorValid){ // initialize after render
26155             this.startMonitoring();
26156         }
26157         this.fireEvent('rendered', this);
26158         return this;
26159     },
26160
26161     /**
26162      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26163      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26164      * object or a valid Roo.DomHelper element config
26165      * @param {Function} handler The function called when the button is clicked
26166      * @param {Object} scope (optional) The scope of the handler function
26167      * @return {Roo.Button}
26168      */
26169     addButton : function(config, handler, scope){
26170         var bc = {
26171             handler: handler,
26172             scope: scope,
26173             minWidth: this.minButtonWidth,
26174             hideParent:true
26175         };
26176         if(typeof config == "string"){
26177             bc.text = config;
26178         }else{
26179             Roo.apply(bc, config);
26180         }
26181         var btn = new Roo.Button(null, bc);
26182         this.buttons.push(btn);
26183         return btn;
26184     },
26185
26186      /**
26187      * Adds a series of form elements (using the xtype property as the factory method.
26188      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26189      * @param {Object} config 
26190      */
26191     
26192     addxtype : function()
26193     {
26194         var ar = Array.prototype.slice.call(arguments, 0);
26195         var ret = false;
26196         for(var i = 0; i < ar.length; i++) {
26197             if (!ar[i]) {
26198                 continue; // skip -- if this happends something invalid got sent, we 
26199                 // should ignore it, as basically that interface element will not show up
26200                 // and that should be pretty obvious!!
26201             }
26202             
26203             if (Roo.form[ar[i].xtype]) {
26204                 ar[i].form = this;
26205                 var fe = Roo.factory(ar[i], Roo.form);
26206                 if (!ret) {
26207                     ret = fe;
26208                 }
26209                 fe.form = this;
26210                 if (fe.store) {
26211                     fe.store.form = this;
26212                 }
26213                 if (fe.isLayout) {  
26214                          
26215                     this.start(fe);
26216                     this.allItems.push(fe);
26217                     if (fe.items && fe.addxtype) {
26218                         fe.addxtype.apply(fe, fe.items);
26219                         delete fe.items;
26220                     }
26221                      this.end();
26222                     continue;
26223                 }
26224                 
26225                 
26226                  
26227                 this.add(fe);
26228               //  console.log('adding ' + ar[i].xtype);
26229             }
26230             if (ar[i].xtype == 'Button') {  
26231                 //console.log('adding button');
26232                 //console.log(ar[i]);
26233                 this.addButton(ar[i]);
26234                 this.allItems.push(fe);
26235                 continue;
26236             }
26237             
26238             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26239                 alert('end is not supported on xtype any more, use items');
26240             //    this.end();
26241             //    //console.log('adding end');
26242             }
26243             
26244         }
26245         return ret;
26246     },
26247     
26248     /**
26249      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26250      * option "monitorValid"
26251      */
26252     startMonitoring : function(){
26253         if(!this.bound){
26254             this.bound = true;
26255             Roo.TaskMgr.start({
26256                 run : this.bindHandler,
26257                 interval : this.monitorPoll || 200,
26258                 scope: this
26259             });
26260         }
26261     },
26262
26263     /**
26264      * Stops monitoring of the valid state of this form
26265      */
26266     stopMonitoring : function(){
26267         this.bound = false;
26268     },
26269
26270     // private
26271     bindHandler : function(){
26272         if(!this.bound){
26273             return false; // stops binding
26274         }
26275         var valid = true;
26276         this.items.each(function(f){
26277             if(!f.isValid(true)){
26278                 valid = false;
26279                 return false;
26280             }
26281         });
26282         for(var i = 0, len = this.buttons.length; i < len; i++){
26283             var btn = this.buttons[i];
26284             if(btn.formBind === true && btn.disabled === valid){
26285                 btn.setDisabled(!valid);
26286             }
26287         }
26288         this.fireEvent('clientvalidation', this, valid);
26289     }
26290     
26291     
26292     
26293     
26294     
26295     
26296     
26297     
26298 });
26299
26300
26301 // back compat
26302 Roo.Form = Roo.form.Form;
26303 /*
26304  * Based on:
26305  * Ext JS Library 1.1.1
26306  * Copyright(c) 2006-2007, Ext JS, LLC.
26307  *
26308  * Originally Released Under LGPL - original licence link has changed is not relivant.
26309  *
26310  * Fork - LGPL
26311  * <script type="text/javascript">
26312  */
26313  
26314  /**
26315  * @class Roo.form.Action
26316  * Internal Class used to handle form actions
26317  * @constructor
26318  * @param {Roo.form.BasicForm} el The form element or its id
26319  * @param {Object} config Configuration options
26320  */
26321  
26322  
26323 // define the action interface
26324 Roo.form.Action = function(form, options){
26325     this.form = form;
26326     this.options = options || {};
26327 };
26328 /**
26329  * Client Validation Failed
26330  * @const 
26331  */
26332 Roo.form.Action.CLIENT_INVALID = 'client';
26333 /**
26334  * Server Validation Failed
26335  * @const 
26336  */
26337  Roo.form.Action.SERVER_INVALID = 'server';
26338  /**
26339  * Connect to Server Failed
26340  * @const 
26341  */
26342 Roo.form.Action.CONNECT_FAILURE = 'connect';
26343 /**
26344  * Reading Data from Server Failed
26345  * @const 
26346  */
26347 Roo.form.Action.LOAD_FAILURE = 'load';
26348
26349 Roo.form.Action.prototype = {
26350     type : 'default',
26351     failureType : undefined,
26352     response : undefined,
26353     result : undefined,
26354
26355     // interface method
26356     run : function(options){
26357
26358     },
26359
26360     // interface method
26361     success : function(response){
26362
26363     },
26364
26365     // interface method
26366     handleResponse : function(response){
26367
26368     },
26369
26370     // default connection failure
26371     failure : function(response){
26372         this.response = response;
26373         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26374         this.form.afterAction(this, false);
26375     },
26376
26377     processResponse : function(response){
26378         this.response = response;
26379         if(!response.responseText){
26380             return true;
26381         }
26382         this.result = this.handleResponse(response);
26383         return this.result;
26384     },
26385
26386     // utility functions used internally
26387     getUrl : function(appendParams){
26388         var url = this.options.url || this.form.url || this.form.el.dom.action;
26389         if(appendParams){
26390             var p = this.getParams();
26391             if(p){
26392                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26393             }
26394         }
26395         return url;
26396     },
26397
26398     getMethod : function(){
26399         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26400     },
26401
26402     getParams : function(){
26403         var bp = this.form.baseParams;
26404         var p = this.options.params;
26405         if(p){
26406             if(typeof p == "object"){
26407                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26408             }else if(typeof p == 'string' && bp){
26409                 p += '&' + Roo.urlEncode(bp);
26410             }
26411         }else if(bp){
26412             p = Roo.urlEncode(bp);
26413         }
26414         return p;
26415     },
26416
26417     createCallback : function(){
26418         return {
26419             success: this.success,
26420             failure: this.failure,
26421             scope: this,
26422             timeout: (this.form.timeout*1000),
26423             upload: this.form.fileUpload ? this.success : undefined
26424         };
26425     }
26426 };
26427
26428 Roo.form.Action.Submit = function(form, options){
26429     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26430 };
26431
26432 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26433     type : 'submit',
26434
26435     run : function(){
26436         var o = this.options;
26437         var method = this.getMethod();
26438         var isPost = method == 'POST';
26439         if(o.clientValidation === false || this.form.isValid()){
26440             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26441                 form:this.form.el.dom,
26442                 url:this.getUrl(!isPost),
26443                 method: method,
26444                 params:isPost ? this.getParams() : null,
26445                 isUpload: this.form.fileUpload
26446             }));
26447
26448         }else if (o.clientValidation !== false){ // client validation failed
26449             this.failureType = Roo.form.Action.CLIENT_INVALID;
26450             this.form.afterAction(this, false);
26451         }
26452     },
26453
26454     success : function(response){
26455         var result = this.processResponse(response);
26456         if(result === true || result.success){
26457             this.form.afterAction(this, true);
26458             return;
26459         }
26460         if(result.errors){
26461             this.form.markInvalid(result.errors);
26462             this.failureType = Roo.form.Action.SERVER_INVALID;
26463         }
26464         this.form.afterAction(this, false);
26465     },
26466
26467     handleResponse : function(response){
26468         if(this.form.errorReader){
26469             var rs = this.form.errorReader.read(response);
26470             var errors = [];
26471             if(rs.records){
26472                 for(var i = 0, len = rs.records.length; i < len; i++) {
26473                     var r = rs.records[i];
26474                     errors[i] = r.data;
26475                 }
26476             }
26477             if(errors.length < 1){
26478                 errors = null;
26479             }
26480             return {
26481                 success : rs.success,
26482                 errors : errors
26483             };
26484         }
26485         var ret = false;
26486         try {
26487             ret = Roo.decode(response.responseText);
26488         } catch (e) {
26489             ret = {
26490                 success: false,
26491                 errorMsg: "Failed to read server message: " + response.responseText,
26492                 errors : []
26493             };
26494         }
26495         return ret;
26496         
26497     }
26498 });
26499
26500
26501 Roo.form.Action.Load = function(form, options){
26502     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26503     this.reader = this.form.reader;
26504 };
26505
26506 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26507     type : 'load',
26508
26509     run : function(){
26510         Roo.Ajax.request(Roo.apply(
26511                 this.createCallback(), {
26512                     method:this.getMethod(),
26513                     url:this.getUrl(false),
26514                     params:this.getParams()
26515         }));
26516     },
26517
26518     success : function(response){
26519         var result = this.processResponse(response);
26520         if(result === true || !result.success || !result.data){
26521             this.failureType = Roo.form.Action.LOAD_FAILURE;
26522             this.form.afterAction(this, false);
26523             return;
26524         }
26525         this.form.clearInvalid();
26526         this.form.setValues(result.data);
26527         this.form.afterAction(this, true);
26528     },
26529
26530     handleResponse : function(response){
26531         if(this.form.reader){
26532             var rs = this.form.reader.read(response);
26533             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26534             return {
26535                 success : rs.success,
26536                 data : data
26537             };
26538         }
26539         return Roo.decode(response.responseText);
26540     }
26541 });
26542
26543 Roo.form.Action.ACTION_TYPES = {
26544     'load' : Roo.form.Action.Load,
26545     'submit' : Roo.form.Action.Submit
26546 };/*
26547  * Based on:
26548  * Ext JS Library 1.1.1
26549  * Copyright(c) 2006-2007, Ext JS, LLC.
26550  *
26551  * Originally Released Under LGPL - original licence link has changed is not relivant.
26552  *
26553  * Fork - LGPL
26554  * <script type="text/javascript">
26555  */
26556  
26557 /**
26558  * @class Roo.form.Layout
26559  * @extends Roo.Component
26560  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26561  * @constructor
26562  * @param {Object} config Configuration options
26563  */
26564 Roo.form.Layout = function(config){
26565     var xitems = [];
26566     if (config.items) {
26567         xitems = config.items;
26568         delete config.items;
26569     }
26570     Roo.form.Layout.superclass.constructor.call(this, config);
26571     this.stack = [];
26572     Roo.each(xitems, this.addxtype, this);
26573      
26574 };
26575
26576 Roo.extend(Roo.form.Layout, Roo.Component, {
26577     /**
26578      * @cfg {String/Object} autoCreate
26579      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26580      */
26581     /**
26582      * @cfg {String/Object/Function} style
26583      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26584      * a function which returns such a specification.
26585      */
26586     /**
26587      * @cfg {String} labelAlign
26588      * Valid values are "left," "top" and "right" (defaults to "left")
26589      */
26590     /**
26591      * @cfg {Number} labelWidth
26592      * Fixed width in pixels of all field labels (defaults to undefined)
26593      */
26594     /**
26595      * @cfg {Boolean} clear
26596      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26597      */
26598     clear : true,
26599     /**
26600      * @cfg {String} labelSeparator
26601      * The separator to use after field labels (defaults to ':')
26602      */
26603     labelSeparator : ':',
26604     /**
26605      * @cfg {Boolean} hideLabels
26606      * True to suppress the display of field labels in this layout (defaults to false)
26607      */
26608     hideLabels : false,
26609
26610     // private
26611     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26612     
26613     isLayout : true,
26614     
26615     // private
26616     onRender : function(ct, position){
26617         if(this.el){ // from markup
26618             this.el = Roo.get(this.el);
26619         }else {  // generate
26620             var cfg = this.getAutoCreate();
26621             this.el = ct.createChild(cfg, position);
26622         }
26623         if(this.style){
26624             this.el.applyStyles(this.style);
26625         }
26626         if(this.labelAlign){
26627             this.el.addClass('x-form-label-'+this.labelAlign);
26628         }
26629         if(this.hideLabels){
26630             this.labelStyle = "display:none";
26631             this.elementStyle = "padding-left:0;";
26632         }else{
26633             if(typeof this.labelWidth == 'number'){
26634                 this.labelStyle = "width:"+this.labelWidth+"px;";
26635                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26636             }
26637             if(this.labelAlign == 'top'){
26638                 this.labelStyle = "width:auto;";
26639                 this.elementStyle = "padding-left:0;";
26640             }
26641         }
26642         var stack = this.stack;
26643         var slen = stack.length;
26644         if(slen > 0){
26645             if(!this.fieldTpl){
26646                 var t = new Roo.Template(
26647                     '<div class="x-form-item {5}">',
26648                         '<label for="{0}" style="{2}">{1}{4}</label>',
26649                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26650                         '</div>',
26651                     '</div><div class="x-form-clear-left"></div>'
26652                 );
26653                 t.disableFormats = true;
26654                 t.compile();
26655                 Roo.form.Layout.prototype.fieldTpl = t;
26656             }
26657             for(var i = 0; i < slen; i++) {
26658                 if(stack[i].isFormField){
26659                     this.renderField(stack[i]);
26660                 }else{
26661                     this.renderComponent(stack[i]);
26662                 }
26663             }
26664         }
26665         if(this.clear){
26666             this.el.createChild({cls:'x-form-clear'});
26667         }
26668     },
26669
26670     // private
26671     renderField : function(f){
26672         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26673                f.id, //0
26674                f.fieldLabel, //1
26675                f.labelStyle||this.labelStyle||'', //2
26676                this.elementStyle||'', //3
26677                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26678                f.itemCls||this.itemCls||''  //5
26679        ], true).getPrevSibling());
26680     },
26681
26682     // private
26683     renderComponent : function(c){
26684         c.render(c.isLayout ? this.el : this.el.createChild());    
26685     },
26686     /**
26687      * Adds a object form elements (using the xtype property as the factory method.)
26688      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26689      * @param {Object} config 
26690      */
26691     addxtype : function(o)
26692     {
26693         // create the lement.
26694         o.form = this.form;
26695         var fe = Roo.factory(o, Roo.form);
26696         this.form.allItems.push(fe);
26697         this.stack.push(fe);
26698         
26699         if (fe.isFormField) {
26700             this.form.items.add(fe);
26701         }
26702          
26703         return fe;
26704     }
26705 });
26706
26707 /**
26708  * @class Roo.form.Column
26709  * @extends Roo.form.Layout
26710  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26711  * @constructor
26712  * @param {Object} config Configuration options
26713  */
26714 Roo.form.Column = function(config){
26715     Roo.form.Column.superclass.constructor.call(this, config);
26716 };
26717
26718 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26719     /**
26720      * @cfg {Number/String} width
26721      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26722      */
26723     /**
26724      * @cfg {String/Object} autoCreate
26725      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26726      */
26727
26728     // private
26729     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26730
26731     // private
26732     onRender : function(ct, position){
26733         Roo.form.Column.superclass.onRender.call(this, ct, position);
26734         if(this.width){
26735             this.el.setWidth(this.width);
26736         }
26737     }
26738 });
26739
26740
26741 /**
26742  * @class Roo.form.Row
26743  * @extends Roo.form.Layout
26744  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26745  * @constructor
26746  * @param {Object} config Configuration options
26747  */
26748
26749  
26750 Roo.form.Row = function(config){
26751     Roo.form.Row.superclass.constructor.call(this, config);
26752 };
26753  
26754 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26755       /**
26756      * @cfg {Number/String} width
26757      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26758      */
26759     /**
26760      * @cfg {Number/String} height
26761      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26762      */
26763     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26764     
26765     padWidth : 20,
26766     // private
26767     onRender : function(ct, position){
26768         //console.log('row render');
26769         if(!this.rowTpl){
26770             var t = new Roo.Template(
26771                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26772                     '<label for="{0}" style="{2}">{1}{4}</label>',
26773                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26774                     '</div>',
26775                 '</div>'
26776             );
26777             t.disableFormats = true;
26778             t.compile();
26779             Roo.form.Layout.prototype.rowTpl = t;
26780         }
26781         this.fieldTpl = this.rowTpl;
26782         
26783         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26784         var labelWidth = 100;
26785         
26786         if ((this.labelAlign != 'top')) {
26787             if (typeof this.labelWidth == 'number') {
26788                 labelWidth = this.labelWidth
26789             }
26790             this.padWidth =  20 + labelWidth;
26791             
26792         }
26793         
26794         Roo.form.Column.superclass.onRender.call(this, ct, position);
26795         if(this.width){
26796             this.el.setWidth(this.width);
26797         }
26798         if(this.height){
26799             this.el.setHeight(this.height);
26800         }
26801     },
26802     
26803     // private
26804     renderField : function(f){
26805         f.fieldEl = this.fieldTpl.append(this.el, [
26806                f.id, f.fieldLabel,
26807                f.labelStyle||this.labelStyle||'',
26808                this.elementStyle||'',
26809                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26810                f.itemCls||this.itemCls||'',
26811                f.width ? f.width + this.padWidth : 160 + this.padWidth
26812        ],true);
26813     }
26814 });
26815  
26816
26817 /**
26818  * @class Roo.form.FieldSet
26819  * @extends Roo.form.Layout
26820  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26821  * @constructor
26822  * @param {Object} config Configuration options
26823  */
26824 Roo.form.FieldSet = function(config){
26825     Roo.form.FieldSet.superclass.constructor.call(this, config);
26826 };
26827
26828 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26829     /**
26830      * @cfg {String} legend
26831      * The text to display as the legend for the FieldSet (defaults to '')
26832      */
26833     /**
26834      * @cfg {String/Object} autoCreate
26835      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26836      */
26837
26838     // private
26839     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26840
26841     // private
26842     onRender : function(ct, position){
26843         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26844         if(this.legend){
26845             this.setLegend(this.legend);
26846         }
26847     },
26848
26849     // private
26850     setLegend : function(text){
26851         if(this.rendered){
26852             this.el.child('legend').update(text);
26853         }
26854     }
26855 });/*
26856  * Based on:
26857  * Ext JS Library 1.1.1
26858  * Copyright(c) 2006-2007, Ext JS, LLC.
26859  *
26860  * Originally Released Under LGPL - original licence link has changed is not relivant.
26861  *
26862  * Fork - LGPL
26863  * <script type="text/javascript">
26864  */
26865 /**
26866  * @class Roo.form.VTypes
26867  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26868  * @singleton
26869  */
26870 Roo.form.VTypes = function(){
26871     // closure these in so they are only created once.
26872     var alpha = /^[a-zA-Z_]+$/;
26873     var alphanum = /^[a-zA-Z0-9_]+$/;
26874     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
26875     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26876
26877     // All these messages and functions are configurable
26878     return {
26879         /**
26880          * The function used to validate email addresses
26881          * @param {String} value The email address
26882          */
26883         'email' : function(v){
26884             return email.test(v);
26885         },
26886         /**
26887          * The error text to display when the email validation function returns false
26888          * @type String
26889          */
26890         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26891         /**
26892          * The keystroke filter mask to be applied on email input
26893          * @type RegExp
26894          */
26895         'emailMask' : /[a-z0-9_\.\-@]/i,
26896
26897         /**
26898          * The function used to validate URLs
26899          * @param {String} value The URL
26900          */
26901         'url' : function(v){
26902             return url.test(v);
26903         },
26904         /**
26905          * The error text to display when the url validation function returns false
26906          * @type String
26907          */
26908         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26909         
26910         /**
26911          * The function used to validate alpha values
26912          * @param {String} value The value
26913          */
26914         'alpha' : function(v){
26915             return alpha.test(v);
26916         },
26917         /**
26918          * The error text to display when the alpha validation function returns false
26919          * @type String
26920          */
26921         'alphaText' : 'This field should only contain letters and _',
26922         /**
26923          * The keystroke filter mask to be applied on alpha input
26924          * @type RegExp
26925          */
26926         'alphaMask' : /[a-z_]/i,
26927
26928         /**
26929          * The function used to validate alphanumeric values
26930          * @param {String} value The value
26931          */
26932         'alphanum' : function(v){
26933             return alphanum.test(v);
26934         },
26935         /**
26936          * The error text to display when the alphanumeric validation function returns false
26937          * @type String
26938          */
26939         'alphanumText' : 'This field should only contain letters, numbers and _',
26940         /**
26941          * The keystroke filter mask to be applied on alphanumeric input
26942          * @type RegExp
26943          */
26944         'alphanumMask' : /[a-z0-9_]/i
26945     };
26946 }();//<script type="text/javascript">
26947
26948 /**
26949  * @class Roo.form.FCKeditor
26950  * @extends Roo.form.TextArea
26951  * Wrapper around the FCKEditor http://www.fckeditor.net
26952  * @constructor
26953  * Creates a new FCKeditor
26954  * @param {Object} config Configuration options
26955  */
26956 Roo.form.FCKeditor = function(config){
26957     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26958     this.addEvents({
26959          /**
26960          * @event editorinit
26961          * Fired when the editor is initialized - you can add extra handlers here..
26962          * @param {FCKeditor} this
26963          * @param {Object} the FCK object.
26964          */
26965         editorinit : true
26966     });
26967     
26968     
26969 };
26970 Roo.form.FCKeditor.editors = { };
26971 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26972 {
26973     //defaultAutoCreate : {
26974     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26975     //},
26976     // private
26977     /**
26978      * @cfg {Object} fck options - see fck manual for details.
26979      */
26980     fckconfig : false,
26981     
26982     /**
26983      * @cfg {Object} fck toolbar set (Basic or Default)
26984      */
26985     toolbarSet : 'Basic',
26986     /**
26987      * @cfg {Object} fck BasePath
26988      */ 
26989     basePath : '/fckeditor/',
26990     
26991     
26992     frame : false,
26993     
26994     value : '',
26995     
26996    
26997     onRender : function(ct, position)
26998     {
26999         if(!this.el){
27000             this.defaultAutoCreate = {
27001                 tag: "textarea",
27002                 style:"width:300px;height:60px;",
27003                 autocomplete: "off"
27004             };
27005         }
27006         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27007         /*
27008         if(this.grow){
27009             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27010             if(this.preventScrollbars){
27011                 this.el.setStyle("overflow", "hidden");
27012             }
27013             this.el.setHeight(this.growMin);
27014         }
27015         */
27016         //console.log('onrender' + this.getId() );
27017         Roo.form.FCKeditor.editors[this.getId()] = this;
27018          
27019
27020         this.replaceTextarea() ;
27021         
27022     },
27023     
27024     getEditor : function() {
27025         return this.fckEditor;
27026     },
27027     /**
27028      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27029      * @param {Mixed} value The value to set
27030      */
27031     
27032     
27033     setValue : function(value)
27034     {
27035         //console.log('setValue: ' + value);
27036         
27037         if(typeof(value) == 'undefined') { // not sure why this is happending...
27038             return;
27039         }
27040         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27041         
27042         //if(!this.el || !this.getEditor()) {
27043         //    this.value = value;
27044             //this.setValue.defer(100,this,[value]);    
27045         //    return;
27046         //} 
27047         
27048         if(!this.getEditor()) {
27049             return;
27050         }
27051         
27052         this.getEditor().SetData(value);
27053         
27054         //
27055
27056     },
27057
27058     /**
27059      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27060      * @return {Mixed} value The field value
27061      */
27062     getValue : function()
27063     {
27064         
27065         if (this.frame && this.frame.dom.style.display == 'none') {
27066             return Roo.form.FCKeditor.superclass.getValue.call(this);
27067         }
27068         
27069         if(!this.el || !this.getEditor()) {
27070            
27071            // this.getValue.defer(100,this); 
27072             return this.value;
27073         }
27074        
27075         
27076         var value=this.getEditor().GetData();
27077         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27078         return Roo.form.FCKeditor.superclass.getValue.call(this);
27079         
27080
27081     },
27082
27083     /**
27084      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27085      * @return {Mixed} value The field value
27086      */
27087     getRawValue : function()
27088     {
27089         if (this.frame && this.frame.dom.style.display == 'none') {
27090             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27091         }
27092         
27093         if(!this.el || !this.getEditor()) {
27094             //this.getRawValue.defer(100,this); 
27095             return this.value;
27096             return;
27097         }
27098         
27099         
27100         
27101         var value=this.getEditor().GetData();
27102         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27103         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27104          
27105     },
27106     
27107     setSize : function(w,h) {
27108         
27109         
27110         
27111         //if (this.frame && this.frame.dom.style.display == 'none') {
27112         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27113         //    return;
27114         //}
27115         //if(!this.el || !this.getEditor()) {
27116         //    this.setSize.defer(100,this, [w,h]); 
27117         //    return;
27118         //}
27119         
27120         
27121         
27122         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27123         
27124         this.frame.dom.setAttribute('width', w);
27125         this.frame.dom.setAttribute('height', h);
27126         this.frame.setSize(w,h);
27127         
27128     },
27129     
27130     toggleSourceEdit : function(value) {
27131         
27132       
27133          
27134         this.el.dom.style.display = value ? '' : 'none';
27135         this.frame.dom.style.display = value ?  'none' : '';
27136         
27137     },
27138     
27139     
27140     focus: function(tag)
27141     {
27142         if (this.frame.dom.style.display == 'none') {
27143             return Roo.form.FCKeditor.superclass.focus.call(this);
27144         }
27145         if(!this.el || !this.getEditor()) {
27146             this.focus.defer(100,this, [tag]); 
27147             return;
27148         }
27149         
27150         
27151         
27152         
27153         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27154         this.getEditor().Focus();
27155         if (tgs.length) {
27156             if (!this.getEditor().Selection.GetSelection()) {
27157                 this.focus.defer(100,this, [tag]); 
27158                 return;
27159             }
27160             
27161             
27162             var r = this.getEditor().EditorDocument.createRange();
27163             r.setStart(tgs[0],0);
27164             r.setEnd(tgs[0],0);
27165             this.getEditor().Selection.GetSelection().removeAllRanges();
27166             this.getEditor().Selection.GetSelection().addRange(r);
27167             this.getEditor().Focus();
27168         }
27169         
27170     },
27171     
27172     
27173     
27174     replaceTextarea : function()
27175     {
27176         if ( document.getElementById( this.getId() + '___Frame' ) )
27177             return ;
27178         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27179         //{
27180             // We must check the elements firstly using the Id and then the name.
27181         var oTextarea = document.getElementById( this.getId() );
27182         
27183         var colElementsByName = document.getElementsByName( this.getId() ) ;
27184          
27185         oTextarea.style.display = 'none' ;
27186
27187         if ( oTextarea.tabIndex ) {            
27188             this.TabIndex = oTextarea.tabIndex ;
27189         }
27190         
27191         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27192         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27193         this.frame = Roo.get(this.getId() + '___Frame')
27194     },
27195     
27196     _getConfigHtml : function()
27197     {
27198         var sConfig = '' ;
27199
27200         for ( var o in this.fckconfig ) {
27201             sConfig += sConfig.length > 0  ? '&amp;' : '';
27202             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27203         }
27204
27205         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27206     },
27207     
27208     
27209     _getIFrameHtml : function()
27210     {
27211         var sFile = 'fckeditor.html' ;
27212         /* no idea what this is about..
27213         try
27214         {
27215             if ( (/fcksource=true/i).test( window.top.location.search ) )
27216                 sFile = 'fckeditor.original.html' ;
27217         }
27218         catch (e) { 
27219         */
27220
27221         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27222         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27223         
27224         
27225         var html = '<iframe id="' + this.getId() +
27226             '___Frame" src="' + sLink +
27227             '" width="' + this.width +
27228             '" height="' + this.height + '"' +
27229             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27230             ' frameborder="0" scrolling="no"></iframe>' ;
27231
27232         return html ;
27233     },
27234     
27235     _insertHtmlBefore : function( html, element )
27236     {
27237         if ( element.insertAdjacentHTML )       {
27238             // IE
27239             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27240         } else { // Gecko
27241             var oRange = document.createRange() ;
27242             oRange.setStartBefore( element ) ;
27243             var oFragment = oRange.createContextualFragment( html );
27244             element.parentNode.insertBefore( oFragment, element ) ;
27245         }
27246     }
27247     
27248     
27249   
27250     
27251     
27252     
27253     
27254
27255 });
27256
27257 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27258
27259 function FCKeditor_OnComplete(editorInstance){
27260     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27261     f.fckEditor = editorInstance;
27262     //console.log("loaded");
27263     f.fireEvent('editorinit', f, editorInstance);
27264
27265   
27266
27267  
27268
27269
27270
27271
27272
27273
27274
27275
27276
27277
27278
27279
27280
27281
27282
27283 //<script type="text/javascript">
27284 /**
27285  * @class Roo.form.GridField
27286  * @extends Roo.form.Field
27287  * Embed a grid (or editable grid into a form)
27288  * STATUS ALPHA
27289  * @constructor
27290  * Creates a new GridField
27291  * @param {Object} config Configuration options
27292  */
27293 Roo.form.GridField = function(config){
27294     Roo.form.GridField.superclass.constructor.call(this, config);
27295      
27296 };
27297
27298 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27299     /**
27300      * @cfg {Number} width  - used to restrict width of grid..
27301      */
27302     width : 100,
27303     /**
27304      * @cfg {Number} height - used to restrict height of grid..
27305      */
27306     height : 50,
27307      /**
27308      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
27309      */
27310     xgrid : false, 
27311     /**
27312      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27313      * {tag: "input", type: "checkbox", autocomplete: "off"})
27314      */
27315    // defaultAutoCreate : { tag: 'div' },
27316     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27317     /**
27318      * @cfg {String} addTitle Text to include for adding a title.
27319      */
27320     addTitle : false,
27321     //
27322     onResize : function(){
27323         Roo.form.Field.superclass.onResize.apply(this, arguments);
27324     },
27325
27326     initEvents : function(){
27327         // Roo.form.Checkbox.superclass.initEvents.call(this);
27328         // has no events...
27329        
27330     },
27331
27332
27333     getResizeEl : function(){
27334         return this.wrap;
27335     },
27336
27337     getPositionEl : function(){
27338         return this.wrap;
27339     },
27340
27341     // private
27342     onRender : function(ct, position){
27343         
27344         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27345         var style = this.style;
27346         delete this.style;
27347         
27348         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
27349         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27350         this.viewEl = this.wrap.createChild({ tag: 'div' });
27351         if (style) {
27352             this.viewEl.applyStyles(style);
27353         }
27354         if (this.width) {
27355             this.viewEl.setWidth(this.width);
27356         }
27357         if (this.height) {
27358             this.viewEl.setHeight(this.height);
27359         }
27360         //if(this.inputValue !== undefined){
27361         //this.setValue(this.value);
27362         
27363         
27364         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27365         
27366         
27367         this.grid.render();
27368         this.grid.getDataSource().on('remove', this.refreshValue, this);
27369         this.grid.getDataSource().on('update', this.refreshValue, this);
27370         this.grid.on('afteredit', this.refreshValue, this);
27371  
27372     },
27373      
27374     
27375     /**
27376      * Sets the value of the item. 
27377      * @param {String} either an object  or a string..
27378      */
27379     setValue : function(v){
27380         //this.value = v;
27381         v = v || []; // empty set..
27382         // this does not seem smart - it really only affects memoryproxy grids..
27383         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27384             var ds = this.grid.getDataSource();
27385             // assumes a json reader..
27386             var data = {}
27387             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27388             ds.loadData( data);
27389         }
27390         Roo.form.GridField.superclass.setValue.call(this, v);
27391         this.refreshValue();
27392         // should load data in the grid really....
27393     },
27394     
27395     // private
27396     refreshValue: function() {
27397          var val = [];
27398         this.grid.getDataSource().each(function(r) {
27399             val.push(r.data);
27400         });
27401         this.el.dom.value = Roo.encode(val);
27402     }
27403     
27404      
27405     
27406     
27407 });//<script type="text/javasscript">
27408  
27409
27410 /**
27411  * @class Roo.DDView
27412  * A DnD enabled version of Roo.View.
27413  * @param {Element/String} container The Element in which to create the View.
27414  * @param {String} tpl The template string used to create the markup for each element of the View
27415  * @param {Object} config The configuration properties. These include all the config options of
27416  * {@link Roo.View} plus some specific to this class.<br>
27417  * <p>
27418  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
27419  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
27420  * <p>
27421  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
27422 .x-view-drag-insert-above {
27423         border-top:1px dotted #3366cc;
27424 }
27425 .x-view-drag-insert-below {
27426         border-bottom:1px dotted #3366cc;
27427 }
27428 </code></pre>
27429  * 
27430  */
27431  
27432 Roo.DDView = function(container, tpl, config) {
27433     Roo.DDView.superclass.constructor.apply(this, arguments);
27434     this.getEl().setStyle("outline", "0px none");
27435     this.getEl().unselectable();
27436     if (this.dragGroup) {
27437                 this.setDraggable(this.dragGroup.split(","));
27438     }
27439     if (this.dropGroup) {
27440                 this.setDroppable(this.dropGroup.split(","));
27441     }
27442     if (this.deletable) {
27443         this.setDeletable();
27444     }
27445     this.isDirtyFlag = false;
27446         this.addEvents({
27447                 "drop" : true
27448         });
27449 };
27450
27451 Roo.extend(Roo.DDView, Roo.View, {
27452 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
27453 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
27454 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
27455 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
27456
27457         isFormField: true,
27458
27459         reset: Roo.emptyFn,
27460         
27461         clearInvalid: Roo.form.Field.prototype.clearInvalid,
27462
27463         validate: function() {
27464                 return true;
27465         },
27466         
27467         destroy: function() {
27468                 this.purgeListeners();
27469                 this.getEl.removeAllListeners();
27470                 this.getEl().remove();
27471                 if (this.dragZone) {
27472                         if (this.dragZone.destroy) {
27473                                 this.dragZone.destroy();
27474                         }
27475                 }
27476                 if (this.dropZone) {
27477                         if (this.dropZone.destroy) {
27478                                 this.dropZone.destroy();
27479                         }
27480                 }
27481         },
27482
27483 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
27484         getName: function() {
27485                 return this.name;
27486         },
27487
27488 /**     Loads the View from a JSON string representing the Records to put into the Store. */
27489         setValue: function(v) {
27490                 if (!this.store) {
27491                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
27492                 }
27493                 var data = {};
27494                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
27495                 this.store.proxy = new Roo.data.MemoryProxy(data);
27496                 this.store.load();
27497         },
27498
27499 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
27500         getValue: function() {
27501                 var result = '(';
27502                 this.store.each(function(rec) {
27503                         result += rec.id + ',';
27504                 });
27505                 return result.substr(0, result.length - 1) + ')';
27506         },
27507         
27508         getIds: function() {
27509                 var i = 0, result = new Array(this.store.getCount());
27510                 this.store.each(function(rec) {
27511                         result[i++] = rec.id;
27512                 });
27513                 return result;
27514         },
27515         
27516         isDirty: function() {
27517                 return this.isDirtyFlag;
27518         },
27519
27520 /**
27521  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
27522  *      whole Element becomes the target, and this causes the drop gesture to append.
27523  */
27524     getTargetFromEvent : function(e) {
27525                 var target = e.getTarget();
27526                 while ((target !== null) && (target.parentNode != this.el.dom)) {
27527                 target = target.parentNode;
27528                 }
27529                 if (!target) {
27530                         target = this.el.dom.lastChild || this.el.dom;
27531                 }
27532                 return target;
27533     },
27534
27535 /**
27536  *      Create the drag data which consists of an object which has the property "ddel" as
27537  *      the drag proxy element. 
27538  */
27539     getDragData : function(e) {
27540         var target = this.findItemFromChild(e.getTarget());
27541                 if(target) {
27542                         this.handleSelection(e);
27543                         var selNodes = this.getSelectedNodes();
27544             var dragData = {
27545                 source: this,
27546                 copy: this.copy || (this.allowCopy && e.ctrlKey),
27547                 nodes: selNodes,
27548                 records: []
27549                         };
27550                         var selectedIndices = this.getSelectedIndexes();
27551                         for (var i = 0; i < selectedIndices.length; i++) {
27552                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
27553                         }
27554                         if (selNodes.length == 1) {
27555                                 dragData.ddel = target.cloneNode(true); // the div element
27556                         } else {
27557                                 var div = document.createElement('div'); // create the multi element drag "ghost"
27558                                 div.className = 'multi-proxy';
27559                                 for (var i = 0, len = selNodes.length; i < len; i++) {
27560                                         div.appendChild(selNodes[i].cloneNode(true));
27561                                 }
27562                                 dragData.ddel = div;
27563                         }
27564             //console.log(dragData)
27565             //console.log(dragData.ddel.innerHTML)
27566                         return dragData;
27567                 }
27568         //console.log('nodragData')
27569                 return false;
27570     },
27571     
27572 /**     Specify to which ddGroup items in this DDView may be dragged. */
27573     setDraggable: function(ddGroup) {
27574         if (ddGroup instanceof Array) {
27575                 Roo.each(ddGroup, this.setDraggable, this);
27576                 return;
27577         }
27578         if (this.dragZone) {
27579                 this.dragZone.addToGroup(ddGroup);
27580         } else {
27581                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
27582                                 containerScroll: true,
27583                                 ddGroup: ddGroup 
27584
27585                         });
27586 //                      Draggability implies selection. DragZone's mousedown selects the element.
27587                         if (!this.multiSelect) { this.singleSelect = true; }
27588
27589 //                      Wire the DragZone's handlers up to methods in *this*
27590                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
27591                 }
27592     },
27593
27594 /**     Specify from which ddGroup this DDView accepts drops. */
27595     setDroppable: function(ddGroup) {
27596         if (ddGroup instanceof Array) {
27597                 Roo.each(ddGroup, this.setDroppable, this);
27598                 return;
27599         }
27600         if (this.dropZone) {
27601                 this.dropZone.addToGroup(ddGroup);
27602         } else {
27603                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
27604                                 containerScroll: true,
27605                                 ddGroup: ddGroup
27606                         });
27607
27608 //                      Wire the DropZone's handlers up to methods in *this*
27609                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
27610                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
27611                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
27612                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
27613                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
27614                 }
27615     },
27616
27617 /**     Decide whether to drop above or below a View node. */
27618     getDropPoint : function(e, n, dd){
27619         if (n == this.el.dom) { return "above"; }
27620                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
27621                 var c = t + (b - t) / 2;
27622                 var y = Roo.lib.Event.getPageY(e);
27623                 if(y <= c) {
27624                         return "above";
27625                 }else{
27626                         return "below";
27627                 }
27628     },
27629
27630     onNodeEnter : function(n, dd, e, data){
27631                 return false;
27632     },
27633     
27634     onNodeOver : function(n, dd, e, data){
27635                 var pt = this.getDropPoint(e, n, dd);
27636                 // set the insert point style on the target node
27637                 var dragElClass = this.dropNotAllowed;
27638                 if (pt) {
27639                         var targetElClass;
27640                         if (pt == "above"){
27641                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
27642                                 targetElClass = "x-view-drag-insert-above";
27643                         } else {
27644                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
27645                                 targetElClass = "x-view-drag-insert-below";
27646                         }
27647                         if (this.lastInsertClass != targetElClass){
27648                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
27649                                 this.lastInsertClass = targetElClass;
27650                         }
27651                 }
27652                 return dragElClass;
27653         },
27654
27655     onNodeOut : function(n, dd, e, data){
27656                 this.removeDropIndicators(n);
27657     },
27658
27659     onNodeDrop : function(n, dd, e, data){
27660         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
27661                 return false;
27662         }
27663         var pt = this.getDropPoint(e, n, dd);
27664                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
27665                 if (pt == "below") { insertAt++; }
27666                 for (var i = 0; i < data.records.length; i++) {
27667                         var r = data.records[i];
27668                         var dup = this.store.getById(r.id);
27669                         if (dup && (dd != this.dragZone)) {
27670                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
27671                         } else {
27672                                 if (data.copy) {
27673                                         this.store.insert(insertAt++, r.copy());
27674                                 } else {
27675                                         data.source.isDirtyFlag = true;
27676                                         r.store.remove(r);
27677                                         this.store.insert(insertAt++, r);
27678                                 }
27679                                 this.isDirtyFlag = true;
27680                         }
27681                 }
27682                 this.dragZone.cachedTarget = null;
27683                 return true;
27684     },
27685
27686     removeDropIndicators : function(n){
27687                 if(n){
27688                         Roo.fly(n).removeClass([
27689                                 "x-view-drag-insert-above",
27690                                 "x-view-drag-insert-below"]);
27691                         this.lastInsertClass = "_noclass";
27692                 }
27693     },
27694
27695 /**
27696  *      Utility method. Add a delete option to the DDView's context menu.
27697  *      @param {String} imageUrl The URL of the "delete" icon image.
27698  */
27699         setDeletable: function(imageUrl) {
27700                 if (!this.singleSelect && !this.multiSelect) {
27701                         this.singleSelect = true;
27702                 }
27703                 var c = this.getContextMenu();
27704                 this.contextMenu.on("itemclick", function(item) {
27705                         switch (item.id) {
27706                                 case "delete":
27707                                         this.remove(this.getSelectedIndexes());
27708                                         break;
27709                         }
27710                 }, this);
27711                 this.contextMenu.add({
27712                         icon: imageUrl,
27713                         id: "delete",
27714                         text: 'Delete'
27715                 });
27716         },
27717         
27718 /**     Return the context menu for this DDView. */
27719         getContextMenu: function() {
27720                 if (!this.contextMenu) {
27721 //                      Create the View's context menu
27722                         this.contextMenu = new Roo.menu.Menu({
27723                                 id: this.id + "-contextmenu"
27724                         });
27725                         this.el.on("contextmenu", this.showContextMenu, this);
27726                 }
27727                 return this.contextMenu;
27728         },
27729         
27730         disableContextMenu: function() {
27731                 if (this.contextMenu) {
27732                         this.el.un("contextmenu", this.showContextMenu, this);
27733                 }
27734         },
27735
27736         showContextMenu: function(e, item) {
27737         item = this.findItemFromChild(e.getTarget());
27738                 if (item) {
27739                         e.stopEvent();
27740                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
27741                         this.contextMenu.showAt(e.getXY());
27742             }
27743     },
27744
27745 /**
27746  *      Remove {@link Roo.data.Record}s at the specified indices.
27747  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
27748  */
27749     remove: function(selectedIndices) {
27750                 selectedIndices = [].concat(selectedIndices);
27751                 for (var i = 0; i < selectedIndices.length; i++) {
27752                         var rec = this.store.getAt(selectedIndices[i]);
27753                         this.store.remove(rec);
27754                 }
27755     },
27756
27757 /**
27758  *      Double click fires the event, but also, if this is draggable, and there is only one other
27759  *      related DropZone, it transfers the selected node.
27760  */
27761     onDblClick : function(e){
27762         var item = this.findItemFromChild(e.getTarget());
27763         if(item){
27764             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
27765                 return false;
27766             }
27767             if (this.dragGroup) {
27768                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
27769                     while (targets.indexOf(this.dropZone) > -1) {
27770                             targets.remove(this.dropZone);
27771                                 }
27772                     if (targets.length == 1) {
27773                                         this.dragZone.cachedTarget = null;
27774                         var el = Roo.get(targets[0].getEl());
27775                         var box = el.getBox(true);
27776                         targets[0].onNodeDrop(el.dom, {
27777                                 target: el.dom,
27778                                 xy: [box.x, box.y + box.height - 1]
27779                         }, null, this.getDragData(e));
27780                     }
27781                 }
27782         }
27783     },
27784     
27785     handleSelection: function(e) {
27786                 this.dragZone.cachedTarget = null;
27787         var item = this.findItemFromChild(e.getTarget());
27788         if (!item) {
27789                 this.clearSelections(true);
27790                 return;
27791         }
27792                 if (item && (this.multiSelect || this.singleSelect)){
27793                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
27794                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
27795                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
27796                                 this.unselect(item);
27797                         } else {
27798                                 this.select(item, this.multiSelect && e.ctrlKey);
27799                                 this.lastSelection = item;
27800                         }
27801                 }
27802     },
27803
27804     onItemClick : function(item, index, e){
27805                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
27806                         return false;
27807                 }
27808                 return true;
27809     },
27810
27811     unselect : function(nodeInfo, suppressEvent){
27812                 var node = this.getNode(nodeInfo);
27813                 if(node && this.isSelected(node)){
27814                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27815                                 Roo.fly(node).removeClass(this.selectedClass);
27816                                 this.selections.remove(node);
27817                                 if(!suppressEvent){
27818                                         this.fireEvent("selectionchange", this, this.selections);
27819                                 }
27820                         }
27821                 }
27822     }
27823 });
27824 /*
27825  * Based on:
27826  * Ext JS Library 1.1.1
27827  * Copyright(c) 2006-2007, Ext JS, LLC.
27828  *
27829  * Originally Released Under LGPL - original licence link has changed is not relivant.
27830  *
27831  * Fork - LGPL
27832  * <script type="text/javascript">
27833  */
27834  
27835 /**
27836  * @class Roo.LayoutManager
27837  * @extends Roo.util.Observable
27838  * Base class for layout managers.
27839  */
27840 Roo.LayoutManager = function(container, config){
27841     Roo.LayoutManager.superclass.constructor.call(this);
27842     this.el = Roo.get(container);
27843     // ie scrollbar fix
27844     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
27845         document.body.scroll = "no";
27846     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
27847         this.el.position('relative');
27848     }
27849     this.id = this.el.id;
27850     this.el.addClass("x-layout-container");
27851     /** false to disable window resize monitoring @type Boolean */
27852     this.monitorWindowResize = true;
27853     this.regions = {};
27854     this.addEvents({
27855         /**
27856          * @event layout
27857          * Fires when a layout is performed. 
27858          * @param {Roo.LayoutManager} this
27859          */
27860         "layout" : true,
27861         /**
27862          * @event regionresized
27863          * Fires when the user resizes a region. 
27864          * @param {Roo.LayoutRegion} region The resized region
27865          * @param {Number} newSize The new size (width for east/west, height for north/south)
27866          */
27867         "regionresized" : true,
27868         /**
27869          * @event regioncollapsed
27870          * Fires when a region is collapsed. 
27871          * @param {Roo.LayoutRegion} region The collapsed region
27872          */
27873         "regioncollapsed" : true,
27874         /**
27875          * @event regionexpanded
27876          * Fires when a region is expanded.  
27877          * @param {Roo.LayoutRegion} region The expanded region
27878          */
27879         "regionexpanded" : true
27880     });
27881     this.updating = false;
27882     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
27883 };
27884
27885 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
27886     /**
27887      * Returns true if this layout is currently being updated
27888      * @return {Boolean}
27889      */
27890     isUpdating : function(){
27891         return this.updating; 
27892     },
27893     
27894     /**
27895      * Suspend the LayoutManager from doing auto-layouts while
27896      * making multiple add or remove calls
27897      */
27898     beginUpdate : function(){
27899         this.updating = true;    
27900     },
27901     
27902     /**
27903      * Restore auto-layouts and optionally disable the manager from performing a layout
27904      * @param {Boolean} noLayout true to disable a layout update 
27905      */
27906     endUpdate : function(noLayout){
27907         this.updating = false;
27908         if(!noLayout){
27909             this.layout();
27910         }    
27911     },
27912     
27913     layout: function(){
27914         
27915     },
27916     
27917     onRegionResized : function(region, newSize){
27918         this.fireEvent("regionresized", region, newSize);
27919         this.layout();
27920     },
27921     
27922     onRegionCollapsed : function(region){
27923         this.fireEvent("regioncollapsed", region);
27924     },
27925     
27926     onRegionExpanded : function(region){
27927         this.fireEvent("regionexpanded", region);
27928     },
27929         
27930     /**
27931      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
27932      * performs box-model adjustments.
27933      * @return {Object} The size as an object {width: (the width), height: (the height)}
27934      */
27935     getViewSize : function(){
27936         var size;
27937         if(this.el.dom != document.body){
27938             size = this.el.getSize();
27939         }else{
27940             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
27941         }
27942         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
27943         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27944         return size;
27945     },
27946     
27947     /**
27948      * Returns the Element this layout is bound to.
27949      * @return {Roo.Element}
27950      */
27951     getEl : function(){
27952         return this.el;
27953     },
27954     
27955     /**
27956      * Returns the specified region.
27957      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
27958      * @return {Roo.LayoutRegion}
27959      */
27960     getRegion : function(target){
27961         return this.regions[target.toLowerCase()];
27962     },
27963     
27964     onWindowResize : function(){
27965         if(this.monitorWindowResize){
27966             this.layout();
27967         }
27968     }
27969 });/*
27970  * Based on:
27971  * Ext JS Library 1.1.1
27972  * Copyright(c) 2006-2007, Ext JS, LLC.
27973  *
27974  * Originally Released Under LGPL - original licence link has changed is not relivant.
27975  *
27976  * Fork - LGPL
27977  * <script type="text/javascript">
27978  */
27979 /**
27980  * @class Roo.BorderLayout
27981  * @extends Roo.LayoutManager
27982  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
27983  * please see: <br><br>
27984  * <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>
27985  * <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>
27986  * Example:
27987  <pre><code>
27988  var layout = new Roo.BorderLayout(document.body, {
27989     north: {
27990         initialSize: 25,
27991         titlebar: false
27992     },
27993     west: {
27994         split:true,
27995         initialSize: 200,
27996         minSize: 175,
27997         maxSize: 400,
27998         titlebar: true,
27999         collapsible: true
28000     },
28001     east: {
28002         split:true,
28003         initialSize: 202,
28004         minSize: 175,
28005         maxSize: 400,
28006         titlebar: true,
28007         collapsible: true
28008     },
28009     south: {
28010         split:true,
28011         initialSize: 100,
28012         minSize: 100,
28013         maxSize: 200,
28014         titlebar: true,
28015         collapsible: true
28016     },
28017     center: {
28018         titlebar: true,
28019         autoScroll:true,
28020         resizeTabs: true,
28021         minTabWidth: 50,
28022         preferredTabWidth: 150
28023     }
28024 });
28025
28026 // shorthand
28027 var CP = Roo.ContentPanel;
28028
28029 layout.beginUpdate();
28030 layout.add("north", new CP("north", "North"));
28031 layout.add("south", new CP("south", {title: "South", closable: true}));
28032 layout.add("west", new CP("west", {title: "West"}));
28033 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28034 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28035 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28036 layout.getRegion("center").showPanel("center1");
28037 layout.endUpdate();
28038 </code></pre>
28039
28040 <b>The container the layout is rendered into can be either the body element or any other element.
28041 If it is not the body element, the container needs to either be an absolute positioned element,
28042 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28043 the container size if it is not the body element.</b>
28044
28045 * @constructor
28046 * Create a new BorderLayout
28047 * @param {String/HTMLElement/Element} container The container this layout is bound to
28048 * @param {Object} config Configuration options
28049  */
28050 Roo.BorderLayout = function(container, config){
28051     config = config || {};
28052     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28053     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28054     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28055         var target = this.factory.validRegions[i];
28056         if(config[target]){
28057             this.addRegion(target, config[target]);
28058         }
28059     }
28060 };
28061
28062 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28063     /**
28064      * Creates and adds a new region if it doesn't already exist.
28065      * @param {String} target The target region key (north, south, east, west or center).
28066      * @param {Object} config The regions config object
28067      * @return {BorderLayoutRegion} The new region
28068      */
28069     addRegion : function(target, config){
28070         if(!this.regions[target]){
28071             var r = this.factory.create(target, this, config);
28072             this.bindRegion(target, r);
28073         }
28074         return this.regions[target];
28075     },
28076
28077     // private (kinda)
28078     bindRegion : function(name, r){
28079         this.regions[name] = r;
28080         r.on("visibilitychange", this.layout, this);
28081         r.on("paneladded", this.layout, this);
28082         r.on("panelremoved", this.layout, this);
28083         r.on("invalidated", this.layout, this);
28084         r.on("resized", this.onRegionResized, this);
28085         r.on("collapsed", this.onRegionCollapsed, this);
28086         r.on("expanded", this.onRegionExpanded, this);
28087     },
28088
28089     /**
28090      * Performs a layout update.
28091      */
28092     layout : function(){
28093         if(this.updating) return;
28094         var size = this.getViewSize();
28095         var w = size.width;
28096         var h = size.height;
28097         var centerW = w;
28098         var centerH = h;
28099         var centerY = 0;
28100         var centerX = 0;
28101         //var x = 0, y = 0;
28102
28103         var rs = this.regions;
28104         var north = rs["north"];
28105         var south = rs["south"]; 
28106         var west = rs["west"];
28107         var east = rs["east"];
28108         var center = rs["center"];
28109         //if(this.hideOnLayout){ // not supported anymore
28110             //c.el.setStyle("display", "none");
28111         //}
28112         if(north && north.isVisible()){
28113             var b = north.getBox();
28114             var m = north.getMargins();
28115             b.width = w - (m.left+m.right);
28116             b.x = m.left;
28117             b.y = m.top;
28118             centerY = b.height + b.y + m.bottom;
28119             centerH -= centerY;
28120             north.updateBox(this.safeBox(b));
28121         }
28122         if(south && south.isVisible()){
28123             var b = south.getBox();
28124             var m = south.getMargins();
28125             b.width = w - (m.left+m.right);
28126             b.x = m.left;
28127             var totalHeight = (b.height + m.top + m.bottom);
28128             b.y = h - totalHeight + m.top;
28129             centerH -= totalHeight;
28130             south.updateBox(this.safeBox(b));
28131         }
28132         if(west && west.isVisible()){
28133             var b = west.getBox();
28134             var m = west.getMargins();
28135             b.height = centerH - (m.top+m.bottom);
28136             b.x = m.left;
28137             b.y = centerY + m.top;
28138             var totalWidth = (b.width + m.left + m.right);
28139             centerX += totalWidth;
28140             centerW -= totalWidth;
28141             west.updateBox(this.safeBox(b));
28142         }
28143         if(east && east.isVisible()){
28144             var b = east.getBox();
28145             var m = east.getMargins();
28146             b.height = centerH - (m.top+m.bottom);
28147             var totalWidth = (b.width + m.left + m.right);
28148             b.x = w - totalWidth + m.left;
28149             b.y = centerY + m.top;
28150             centerW -= totalWidth;
28151             east.updateBox(this.safeBox(b));
28152         }
28153         if(center){
28154             var m = center.getMargins();
28155             var centerBox = {
28156                 x: centerX + m.left,
28157                 y: centerY + m.top,
28158                 width: centerW - (m.left+m.right),
28159                 height: centerH - (m.top+m.bottom)
28160             };
28161             //if(this.hideOnLayout){
28162                 //center.el.setStyle("display", "block");
28163             //}
28164             center.updateBox(this.safeBox(centerBox));
28165         }
28166         this.el.repaint();
28167         this.fireEvent("layout", this);
28168     },
28169
28170     // private
28171     safeBox : function(box){
28172         box.width = Math.max(0, box.width);
28173         box.height = Math.max(0, box.height);
28174         return box;
28175     },
28176
28177     /**
28178      * Adds a ContentPanel (or subclass) to this layout.
28179      * @param {String} target The target region key (north, south, east, west or center).
28180      * @param {Roo.ContentPanel} panel The panel to add
28181      * @return {Roo.ContentPanel} The added panel
28182      */
28183     add : function(target, panel){
28184          
28185         target = target.toLowerCase();
28186         return this.regions[target].add(panel);
28187     },
28188
28189     /**
28190      * Remove a ContentPanel (or subclass) to this layout.
28191      * @param {String} target The target region key (north, south, east, west or center).
28192      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28193      * @return {Roo.ContentPanel} The removed panel
28194      */
28195     remove : function(target, panel){
28196         target = target.toLowerCase();
28197         return this.regions[target].remove(panel);
28198     },
28199
28200     /**
28201      * Searches all regions for a panel with the specified id
28202      * @param {String} panelId
28203      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28204      */
28205     findPanel : function(panelId){
28206         var rs = this.regions;
28207         for(var target in rs){
28208             if(typeof rs[target] != "function"){
28209                 var p = rs[target].getPanel(panelId);
28210                 if(p){
28211                     return p;
28212                 }
28213             }
28214         }
28215         return null;
28216     },
28217
28218     /**
28219      * Searches all regions for a panel with the specified id and activates (shows) it.
28220      * @param {String/ContentPanel} panelId The panels id or the panel itself
28221      * @return {Roo.ContentPanel} The shown panel or null
28222      */
28223     showPanel : function(panelId) {
28224       var rs = this.regions;
28225       for(var target in rs){
28226          var r = rs[target];
28227          if(typeof r != "function"){
28228             if(r.hasPanel(panelId)){
28229                return r.showPanel(panelId);
28230             }
28231          }
28232       }
28233       return null;
28234    },
28235
28236    /**
28237      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28238      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28239      */
28240     restoreState : function(provider){
28241         if(!provider){
28242             provider = Roo.state.Manager;
28243         }
28244         var sm = new Roo.LayoutStateManager();
28245         sm.init(this, provider);
28246     },
28247
28248     /**
28249      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28250      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28251      * a valid ContentPanel config object.  Example:
28252      * <pre><code>
28253 // Create the main layout
28254 var layout = new Roo.BorderLayout('main-ct', {
28255     west: {
28256         split:true,
28257         minSize: 175,
28258         titlebar: true
28259     },
28260     center: {
28261         title:'Components'
28262     }
28263 }, 'main-ct');
28264
28265 // Create and add multiple ContentPanels at once via configs
28266 layout.batchAdd({
28267    west: {
28268        id: 'source-files',
28269        autoCreate:true,
28270        title:'Ext Source Files',
28271        autoScroll:true,
28272        fitToFrame:true
28273    },
28274    center : {
28275        el: cview,
28276        autoScroll:true,
28277        fitToFrame:true,
28278        toolbar: tb,
28279        resizeEl:'cbody'
28280    }
28281 });
28282 </code></pre>
28283      * @param {Object} regions An object containing ContentPanel configs by region name
28284      */
28285     batchAdd : function(regions){
28286         this.beginUpdate();
28287         for(var rname in regions){
28288             var lr = this.regions[rname];
28289             if(lr){
28290                 this.addTypedPanels(lr, regions[rname]);
28291             }
28292         }
28293         this.endUpdate();
28294     },
28295
28296     // private
28297     addTypedPanels : function(lr, ps){
28298         if(typeof ps == 'string'){
28299             lr.add(new Roo.ContentPanel(ps));
28300         }
28301         else if(ps instanceof Array){
28302             for(var i =0, len = ps.length; i < len; i++){
28303                 this.addTypedPanels(lr, ps[i]);
28304             }
28305         }
28306         else if(!ps.events){ // raw config?
28307             var el = ps.el;
28308             delete ps.el; // prevent conflict
28309             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28310         }
28311         else {  // panel object assumed!
28312             lr.add(ps);
28313         }
28314     },
28315     /**
28316      * Adds a xtype elements to the layout.
28317      * <pre><code>
28318
28319 layout.addxtype({
28320        xtype : 'ContentPanel',
28321        region: 'west',
28322        items: [ .... ]
28323    }
28324 );
28325
28326 layout.addxtype({
28327         xtype : 'NestedLayoutPanel',
28328         region: 'west',
28329         layout: {
28330            center: { },
28331            west: { }   
28332         },
28333         items : [ ... list of content panels or nested layout panels.. ]
28334    }
28335 );
28336 </code></pre>
28337      * @param {Object} cfg Xtype definition of item to add.
28338      */
28339     addxtype : function(cfg)
28340     {
28341         // basically accepts a pannel...
28342         // can accept a layout region..!?!?
28343        // console.log('BorderLayout add ' + cfg.xtype)
28344         
28345         if (!cfg.xtype.match(/Panel$/)) {
28346             return false;
28347         }
28348         var ret = false;
28349         var region = cfg.region;
28350         delete cfg.region;
28351         
28352           
28353         var xitems = [];
28354         if (cfg.items) {
28355             xitems = cfg.items;
28356             delete cfg.items;
28357         }
28358         
28359         
28360         switch(cfg.xtype) 
28361         {
28362             case 'ContentPanel':  // ContentPanel (el, cfg)
28363                 if(cfg.autoCreate) {
28364                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28365                 } else {
28366                     var el = this.el.createChild();
28367                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28368                 }
28369                 
28370                 this.add(region, ret);
28371                 break;
28372             
28373             
28374             case 'TreePanel': // our new panel!
28375                 cfg.el = this.el.createChild();
28376                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28377                 this.add(region, ret);
28378                 break;
28379             
28380             case 'NestedLayoutPanel': 
28381                 // create a new Layout (which is  a Border Layout...
28382                 var el = this.el.createChild();
28383                 var clayout = cfg.layout;
28384                 delete cfg.layout;
28385                 clayout.items   = clayout.items  || [];
28386                 // replace this exitems with the clayout ones..
28387                 xitems = clayout.items;
28388                  
28389                 
28390                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
28391                     cfg.background = false;
28392                 }
28393                 var layout = new Roo.BorderLayout(el, clayout);
28394                 
28395                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
28396                 //console.log('adding nested layout panel '  + cfg.toSource());
28397                 this.add(region, ret);
28398                 
28399                 break;
28400                 
28401             case 'GridPanel': 
28402             
28403                 // needs grid and region
28404                 
28405                 //var el = this.getRegion(region).el.createChild();
28406                 var el = this.el.createChild();
28407                 // create the grid first...
28408                 
28409                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
28410                 delete cfg.grid;
28411                 if (region == 'center' && this.active ) {
28412                     cfg.background = false;
28413                 }
28414                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
28415                 
28416                 this.add(region, ret);
28417                 if (cfg.background) {
28418                     ret.on('activate', function(gp) {
28419                         if (!gp.grid.rendered) {
28420                             gp.grid.render();
28421                         }
28422                     });
28423                 } else {
28424                     grid.render();
28425                 }
28426                 break;
28427            
28428                
28429                 
28430                 
28431             default: 
28432                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
28433                 return;
28434              // GridPanel (grid, cfg)
28435             
28436         }
28437         this.beginUpdate();
28438         // add children..
28439         Roo.each(xitems, function(i)  {
28440             ret.addxtype(i);
28441         });
28442         this.endUpdate();
28443         return ret;
28444         
28445     }
28446 });
28447
28448 /**
28449  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
28450  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
28451  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
28452  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
28453  * <pre><code>
28454 // shorthand
28455 var CP = Roo.ContentPanel;
28456
28457 var layout = Roo.BorderLayout.create({
28458     north: {
28459         initialSize: 25,
28460         titlebar: false,
28461         panels: [new CP("north", "North")]
28462     },
28463     west: {
28464         split:true,
28465         initialSize: 200,
28466         minSize: 175,
28467         maxSize: 400,
28468         titlebar: true,
28469         collapsible: true,
28470         panels: [new CP("west", {title: "West"})]
28471     },
28472     east: {
28473         split:true,
28474         initialSize: 202,
28475         minSize: 175,
28476         maxSize: 400,
28477         titlebar: true,
28478         collapsible: true,
28479         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
28480     },
28481     south: {
28482         split:true,
28483         initialSize: 100,
28484         minSize: 100,
28485         maxSize: 200,
28486         titlebar: true,
28487         collapsible: true,
28488         panels: [new CP("south", {title: "South", closable: true})]
28489     },
28490     center: {
28491         titlebar: true,
28492         autoScroll:true,
28493         resizeTabs: true,
28494         minTabWidth: 50,
28495         preferredTabWidth: 150,
28496         panels: [
28497             new CP("center1", {title: "Close Me", closable: true}),
28498             new CP("center2", {title: "Center Panel", closable: false})
28499         ]
28500     }
28501 }, document.body);
28502
28503 layout.getRegion("center").showPanel("center1");
28504 </code></pre>
28505  * @param config
28506  * @param targetEl
28507  */
28508 Roo.BorderLayout.create = function(config, targetEl){
28509     var layout = new Roo.BorderLayout(targetEl || document.body, config);
28510     layout.beginUpdate();
28511     var regions = Roo.BorderLayout.RegionFactory.validRegions;
28512     for(var j = 0, jlen = regions.length; j < jlen; j++){
28513         var lr = regions[j];
28514         if(layout.regions[lr] && config[lr].panels){
28515             var r = layout.regions[lr];
28516             var ps = config[lr].panels;
28517             layout.addTypedPanels(r, ps);
28518         }
28519     }
28520     layout.endUpdate();
28521     return layout;
28522 };
28523
28524 // private
28525 Roo.BorderLayout.RegionFactory = {
28526     // private
28527     validRegions : ["north","south","east","west","center"],
28528
28529     // private
28530     create : function(target, mgr, config){
28531         target = target.toLowerCase();
28532         if(config.lightweight || config.basic){
28533             return new Roo.BasicLayoutRegion(mgr, config, target);
28534         }
28535         switch(target){
28536             case "north":
28537                 return new Roo.NorthLayoutRegion(mgr, config);
28538             case "south":
28539                 return new Roo.SouthLayoutRegion(mgr, config);
28540             case "east":
28541                 return new Roo.EastLayoutRegion(mgr, config);
28542             case "west":
28543                 return new Roo.WestLayoutRegion(mgr, config);
28544             case "center":
28545                 return new Roo.CenterLayoutRegion(mgr, config);
28546         }
28547         throw 'Layout region "'+target+'" not supported.';
28548     }
28549 };/*
28550  * Based on:
28551  * Ext JS Library 1.1.1
28552  * Copyright(c) 2006-2007, Ext JS, LLC.
28553  *
28554  * Originally Released Under LGPL - original licence link has changed is not relivant.
28555  *
28556  * Fork - LGPL
28557  * <script type="text/javascript">
28558  */
28559  
28560 /**
28561  * @class Roo.BasicLayoutRegion
28562  * @extends Roo.util.Observable
28563  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
28564  * and does not have a titlebar, tabs or any other features. All it does is size and position 
28565  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
28566  */
28567 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
28568     this.mgr = mgr;
28569     this.position  = pos;
28570     this.events = {
28571         /**
28572          * @scope Roo.BasicLayoutRegion
28573          */
28574         
28575         /**
28576          * @event beforeremove
28577          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
28578          * @param {Roo.LayoutRegion} this
28579          * @param {Roo.ContentPanel} panel The panel
28580          * @param {Object} e The cancel event object
28581          */
28582         "beforeremove" : true,
28583         /**
28584          * @event invalidated
28585          * Fires when the layout for this region is changed.
28586          * @param {Roo.LayoutRegion} this
28587          */
28588         "invalidated" : true,
28589         /**
28590          * @event visibilitychange
28591          * Fires when this region is shown or hidden 
28592          * @param {Roo.LayoutRegion} this
28593          * @param {Boolean} visibility true or false
28594          */
28595         "visibilitychange" : true,
28596         /**
28597          * @event paneladded
28598          * Fires when a panel is added. 
28599          * @param {Roo.LayoutRegion} this
28600          * @param {Roo.ContentPanel} panel The panel
28601          */
28602         "paneladded" : true,
28603         /**
28604          * @event panelremoved
28605          * Fires when a panel is removed. 
28606          * @param {Roo.LayoutRegion} this
28607          * @param {Roo.ContentPanel} panel The panel
28608          */
28609         "panelremoved" : true,
28610         /**
28611          * @event collapsed
28612          * Fires when this region is collapsed.
28613          * @param {Roo.LayoutRegion} this
28614          */
28615         "collapsed" : true,
28616         /**
28617          * @event expanded
28618          * Fires when this region is expanded.
28619          * @param {Roo.LayoutRegion} this
28620          */
28621         "expanded" : true,
28622         /**
28623          * @event slideshow
28624          * Fires when this region is slid into view.
28625          * @param {Roo.LayoutRegion} this
28626          */
28627         "slideshow" : true,
28628         /**
28629          * @event slidehide
28630          * Fires when this region slides out of view. 
28631          * @param {Roo.LayoutRegion} this
28632          */
28633         "slidehide" : true,
28634         /**
28635          * @event panelactivated
28636          * Fires when a panel is activated. 
28637          * @param {Roo.LayoutRegion} this
28638          * @param {Roo.ContentPanel} panel The activated panel
28639          */
28640         "panelactivated" : true,
28641         /**
28642          * @event resized
28643          * Fires when the user resizes this region. 
28644          * @param {Roo.LayoutRegion} this
28645          * @param {Number} newSize The new size (width for east/west, height for north/south)
28646          */
28647         "resized" : true
28648     };
28649     /** A collection of panels in this region. @type Roo.util.MixedCollection */
28650     this.panels = new Roo.util.MixedCollection();
28651     this.panels.getKey = this.getPanelId.createDelegate(this);
28652     this.box = null;
28653     this.activePanel = null;
28654     // ensure listeners are added...
28655     
28656     if (config.listeners || config.events) {
28657         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
28658             listeners : config.listeners || {},
28659             events : config.events || {}
28660         });
28661     }
28662     
28663     if(skipConfig !== true){
28664         this.applyConfig(config);
28665     }
28666 };
28667
28668 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
28669     getPanelId : function(p){
28670         return p.getId();
28671     },
28672     
28673     applyConfig : function(config){
28674         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
28675         this.config = config;
28676         
28677     },
28678     
28679     /**
28680      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
28681      * the width, for horizontal (north, south) the height.
28682      * @param {Number} newSize The new width or height
28683      */
28684     resizeTo : function(newSize){
28685         var el = this.el ? this.el :
28686                  (this.activePanel ? this.activePanel.getEl() : null);
28687         if(el){
28688             switch(this.position){
28689                 case "east":
28690                 case "west":
28691                     el.setWidth(newSize);
28692                     this.fireEvent("resized", this, newSize);
28693                 break;
28694                 case "north":
28695                 case "south":
28696                     el.setHeight(newSize);
28697                     this.fireEvent("resized", this, newSize);
28698                 break;                
28699             }
28700         }
28701     },
28702     
28703     getBox : function(){
28704         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
28705     },
28706     
28707     getMargins : function(){
28708         return this.margins;
28709     },
28710     
28711     updateBox : function(box){
28712         this.box = box;
28713         var el = this.activePanel.getEl();
28714         el.dom.style.left = box.x + "px";
28715         el.dom.style.top = box.y + "px";
28716         this.activePanel.setSize(box.width, box.height);
28717     },
28718     
28719     /**
28720      * Returns the container element for this region.
28721      * @return {Roo.Element}
28722      */
28723     getEl : function(){
28724         return this.activePanel;
28725     },
28726     
28727     /**
28728      * Returns true if this region is currently visible.
28729      * @return {Boolean}
28730      */
28731     isVisible : function(){
28732         return this.activePanel ? true : false;
28733     },
28734     
28735     setActivePanel : function(panel){
28736         panel = this.getPanel(panel);
28737         if(this.activePanel && this.activePanel != panel){
28738             this.activePanel.setActiveState(false);
28739             this.activePanel.getEl().setLeftTop(-10000,-10000);
28740         }
28741         this.activePanel = panel;
28742         panel.setActiveState(true);
28743         if(this.box){
28744             panel.setSize(this.box.width, this.box.height);
28745         }
28746         this.fireEvent("panelactivated", this, panel);
28747         this.fireEvent("invalidated");
28748     },
28749     
28750     /**
28751      * Show the specified panel.
28752      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
28753      * @return {Roo.ContentPanel} The shown panel or null
28754      */
28755     showPanel : function(panel){
28756         if(panel = this.getPanel(panel)){
28757             this.setActivePanel(panel);
28758         }
28759         return panel;
28760     },
28761     
28762     /**
28763      * Get the active panel for this region.
28764      * @return {Roo.ContentPanel} The active panel or null
28765      */
28766     getActivePanel : function(){
28767         return this.activePanel;
28768     },
28769     
28770     /**
28771      * Add the passed ContentPanel(s)
28772      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
28773      * @return {Roo.ContentPanel} The panel added (if only one was added)
28774      */
28775     add : function(panel){
28776         if(arguments.length > 1){
28777             for(var i = 0, len = arguments.length; i < len; i++) {
28778                 this.add(arguments[i]);
28779             }
28780             return null;
28781         }
28782         if(this.hasPanel(panel)){
28783             this.showPanel(panel);
28784             return panel;
28785         }
28786         var el = panel.getEl();
28787         if(el.dom.parentNode != this.mgr.el.dom){
28788             this.mgr.el.dom.appendChild(el.dom);
28789         }
28790         if(panel.setRegion){
28791             panel.setRegion(this);
28792         }
28793         this.panels.add(panel);
28794         el.setStyle("position", "absolute");
28795         if(!panel.background){
28796             this.setActivePanel(panel);
28797             if(this.config.initialSize && this.panels.getCount()==1){
28798                 this.resizeTo(this.config.initialSize);
28799             }
28800         }
28801         this.fireEvent("paneladded", this, panel);
28802         return panel;
28803     },
28804     
28805     /**
28806      * Returns true if the panel is in this region.
28807      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28808      * @return {Boolean}
28809      */
28810     hasPanel : function(panel){
28811         if(typeof panel == "object"){ // must be panel obj
28812             panel = panel.getId();
28813         }
28814         return this.getPanel(panel) ? true : false;
28815     },
28816     
28817     /**
28818      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
28819      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28820      * @param {Boolean} preservePanel Overrides the config preservePanel option
28821      * @return {Roo.ContentPanel} The panel that was removed
28822      */
28823     remove : function(panel, preservePanel){
28824         panel = this.getPanel(panel);
28825         if(!panel){
28826             return null;
28827         }
28828         var e = {};
28829         this.fireEvent("beforeremove", this, panel, e);
28830         if(e.cancel === true){
28831             return null;
28832         }
28833         var panelId = panel.getId();
28834         this.panels.removeKey(panelId);
28835         return panel;
28836     },
28837     
28838     /**
28839      * Returns the panel specified or null if it's not in this region.
28840      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28841      * @return {Roo.ContentPanel}
28842      */
28843     getPanel : function(id){
28844         if(typeof id == "object"){ // must be panel obj
28845             return id;
28846         }
28847         return this.panels.get(id);
28848     },
28849     
28850     /**
28851      * Returns this regions position (north/south/east/west/center).
28852      * @return {String} 
28853      */
28854     getPosition: function(){
28855         return this.position;    
28856     }
28857 });/*
28858  * Based on:
28859  * Ext JS Library 1.1.1
28860  * Copyright(c) 2006-2007, Ext JS, LLC.
28861  *
28862  * Originally Released Under LGPL - original licence link has changed is not relivant.
28863  *
28864  * Fork - LGPL
28865  * <script type="text/javascript">
28866  */
28867  
28868 /**
28869  * @class Roo.LayoutRegion
28870  * @extends Roo.BasicLayoutRegion
28871  * This class represents a region in a layout manager.
28872  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
28873  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
28874  * @cfg {Boolean} floatable False to disable floating (defaults to true)
28875  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
28876  * @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})
28877  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
28878  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
28879  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
28880  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
28881  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
28882  * @cfg {String} title The title for the region (overrides panel titles)
28883  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
28884  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
28885  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
28886  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
28887  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
28888  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
28889  * the space available, similar to FireFox 1.5 tabs (defaults to false)
28890  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
28891  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
28892  * @cfg {Boolean} showPin True to show a pin button
28893 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
28894 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
28895 * @cfg {Boolean} disableTabTips True to disable tab tooltips
28896 * @cfg {Number} width  For East/West panels
28897 * @cfg {Number} height For North/South panels
28898 * @cfg {Boolean} split To show the splitter
28899  */
28900 Roo.LayoutRegion = function(mgr, config, pos){
28901     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
28902     var dh = Roo.DomHelper;
28903     /** This region's container element 
28904     * @type Roo.Element */
28905     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
28906     /** This region's title element 
28907     * @type Roo.Element */
28908
28909     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
28910         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
28911         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
28912     ]}, true);
28913     this.titleEl.enableDisplayMode();
28914     /** This region's title text element 
28915     * @type HTMLElement */
28916     this.titleTextEl = this.titleEl.dom.firstChild;
28917     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
28918     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
28919     this.closeBtn.enableDisplayMode();
28920     this.closeBtn.on("click", this.closeClicked, this);
28921     this.closeBtn.hide();
28922
28923     this.createBody(config);
28924     this.visible = true;
28925     this.collapsed = false;
28926
28927     if(config.hideWhenEmpty){
28928         this.hide();
28929         this.on("paneladded", this.validateVisibility, this);
28930         this.on("panelremoved", this.validateVisibility, this);
28931     }
28932     this.applyConfig(config);
28933 };
28934
28935 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
28936
28937     createBody : function(){
28938         /** This region's body element 
28939         * @type Roo.Element */
28940         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
28941     },
28942
28943     applyConfig : function(c){
28944         if(c.collapsible && this.position != "center" && !this.collapsedEl){
28945             var dh = Roo.DomHelper;
28946             if(c.titlebar !== false){
28947                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
28948                 this.collapseBtn.on("click", this.collapse, this);
28949                 this.collapseBtn.enableDisplayMode();
28950
28951                 if(c.showPin === true || this.showPin){
28952                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
28953                     this.stickBtn.enableDisplayMode();
28954                     this.stickBtn.on("click", this.expand, this);
28955                     this.stickBtn.hide();
28956                 }
28957             }
28958             /** This region's collapsed element
28959             * @type Roo.Element */
28960             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
28961                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
28962             ]}, true);
28963             if(c.floatable !== false){
28964                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
28965                this.collapsedEl.on("click", this.collapseClick, this);
28966             }
28967
28968             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
28969                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
28970                    id: "message", unselectable: "on", style:{"float":"left"}});
28971                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
28972              }
28973             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
28974             this.expandBtn.on("click", this.expand, this);
28975         }
28976         if(this.collapseBtn){
28977             this.collapseBtn.setVisible(c.collapsible == true);
28978         }
28979         this.cmargins = c.cmargins || this.cmargins ||
28980                          (this.position == "west" || this.position == "east" ?
28981                              {top: 0, left: 2, right:2, bottom: 0} :
28982                              {top: 2, left: 0, right:0, bottom: 2});
28983         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
28984         this.bottomTabs = c.tabPosition != "top";
28985         this.autoScroll = c.autoScroll || false;
28986         if(this.autoScroll){
28987             this.bodyEl.setStyle("overflow", "auto");
28988         }else{
28989             this.bodyEl.setStyle("overflow", "hidden");
28990         }
28991         //if(c.titlebar !== false){
28992             if((!c.titlebar && !c.title) || c.titlebar === false){
28993                 this.titleEl.hide();
28994             }else{
28995                 this.titleEl.show();
28996                 if(c.title){
28997                     this.titleTextEl.innerHTML = c.title;
28998                 }
28999             }
29000         //}
29001         this.duration = c.duration || .30;
29002         this.slideDuration = c.slideDuration || .45;
29003         this.config = c;
29004         if(c.collapsed){
29005             this.collapse(true);
29006         }
29007         if(c.hidden){
29008             this.hide();
29009         }
29010     },
29011     /**
29012      * Returns true if this region is currently visible.
29013      * @return {Boolean}
29014      */
29015     isVisible : function(){
29016         return this.visible;
29017     },
29018
29019     /**
29020      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29021      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29022      */
29023     setCollapsedTitle : function(title){
29024         title = title || "&#160;";
29025         if(this.collapsedTitleTextEl){
29026             this.collapsedTitleTextEl.innerHTML = title;
29027         }
29028     },
29029
29030     getBox : function(){
29031         var b;
29032         if(!this.collapsed){
29033             b = this.el.getBox(false, true);
29034         }else{
29035             b = this.collapsedEl.getBox(false, true);
29036         }
29037         return b;
29038     },
29039
29040     getMargins : function(){
29041         return this.collapsed ? this.cmargins : this.margins;
29042     },
29043
29044     highlight : function(){
29045         this.el.addClass("x-layout-panel-dragover");
29046     },
29047
29048     unhighlight : function(){
29049         this.el.removeClass("x-layout-panel-dragover");
29050     },
29051
29052     updateBox : function(box){
29053         this.box = box;
29054         if(!this.collapsed){
29055             this.el.dom.style.left = box.x + "px";
29056             this.el.dom.style.top = box.y + "px";
29057             this.updateBody(box.width, box.height);
29058         }else{
29059             this.collapsedEl.dom.style.left = box.x + "px";
29060             this.collapsedEl.dom.style.top = box.y + "px";
29061             this.collapsedEl.setSize(box.width, box.height);
29062         }
29063         if(this.tabs){
29064             this.tabs.autoSizeTabs();
29065         }
29066     },
29067
29068     updateBody : function(w, h){
29069         if(w !== null){
29070             this.el.setWidth(w);
29071             w -= this.el.getBorderWidth("rl");
29072             if(this.config.adjustments){
29073                 w += this.config.adjustments[0];
29074             }
29075         }
29076         if(h !== null){
29077             this.el.setHeight(h);
29078             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29079             h -= this.el.getBorderWidth("tb");
29080             if(this.config.adjustments){
29081                 h += this.config.adjustments[1];
29082             }
29083             this.bodyEl.setHeight(h);
29084             if(this.tabs){
29085                 h = this.tabs.syncHeight(h);
29086             }
29087         }
29088         if(this.panelSize){
29089             w = w !== null ? w : this.panelSize.width;
29090             h = h !== null ? h : this.panelSize.height;
29091         }
29092         if(this.activePanel){
29093             var el = this.activePanel.getEl();
29094             w = w !== null ? w : el.getWidth();
29095             h = h !== null ? h : el.getHeight();
29096             this.panelSize = {width: w, height: h};
29097             this.activePanel.setSize(w, h);
29098         }
29099         if(Roo.isIE && this.tabs){
29100             this.tabs.el.repaint();
29101         }
29102     },
29103
29104     /**
29105      * Returns the container element for this region.
29106      * @return {Roo.Element}
29107      */
29108     getEl : function(){
29109         return this.el;
29110     },
29111
29112     /**
29113      * Hides this region.
29114      */
29115     hide : function(){
29116         if(!this.collapsed){
29117             this.el.dom.style.left = "-2000px";
29118             this.el.hide();
29119         }else{
29120             this.collapsedEl.dom.style.left = "-2000px";
29121             this.collapsedEl.hide();
29122         }
29123         this.visible = false;
29124         this.fireEvent("visibilitychange", this, false);
29125     },
29126
29127     /**
29128      * Shows this region if it was previously hidden.
29129      */
29130     show : function(){
29131         if(!this.collapsed){
29132             this.el.show();
29133         }else{
29134             this.collapsedEl.show();
29135         }
29136         this.visible = true;
29137         this.fireEvent("visibilitychange", this, true);
29138     },
29139
29140     closeClicked : function(){
29141         if(this.activePanel){
29142             this.remove(this.activePanel);
29143         }
29144     },
29145
29146     collapseClick : function(e){
29147         if(this.isSlid){
29148            e.stopPropagation();
29149            this.slideIn();
29150         }else{
29151            e.stopPropagation();
29152            this.slideOut();
29153         }
29154     },
29155
29156     /**
29157      * Collapses this region.
29158      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29159      */
29160     collapse : function(skipAnim){
29161         if(this.collapsed) return;
29162         this.collapsed = true;
29163         if(this.split){
29164             this.split.el.hide();
29165         }
29166         if(this.config.animate && skipAnim !== true){
29167             this.fireEvent("invalidated", this);
29168             this.animateCollapse();
29169         }else{
29170             this.el.setLocation(-20000,-20000);
29171             this.el.hide();
29172             this.collapsedEl.show();
29173             this.fireEvent("collapsed", this);
29174             this.fireEvent("invalidated", this);
29175         }
29176     },
29177
29178     animateCollapse : function(){
29179         // overridden
29180     },
29181
29182     /**
29183      * Expands this region if it was previously collapsed.
29184      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29185      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29186      */
29187     expand : function(e, skipAnim){
29188         if(e) e.stopPropagation();
29189         if(!this.collapsed || this.el.hasActiveFx()) return;
29190         if(this.isSlid){
29191             this.afterSlideIn();
29192             skipAnim = true;
29193         }
29194         this.collapsed = false;
29195         if(this.config.animate && skipAnim !== true){
29196             this.animateExpand();
29197         }else{
29198             this.el.show();
29199             if(this.split){
29200                 this.split.el.show();
29201             }
29202             this.collapsedEl.setLocation(-2000,-2000);
29203             this.collapsedEl.hide();
29204             this.fireEvent("invalidated", this);
29205             this.fireEvent("expanded", this);
29206         }
29207     },
29208
29209     animateExpand : function(){
29210         // overridden
29211     },
29212
29213     initTabs : function(){
29214         this.bodyEl.setStyle("overflow", "hidden");
29215         var ts = new Roo.TabPanel(this.bodyEl.dom, {
29216             tabPosition: this.bottomTabs ? 'bottom' : 'top',
29217             disableTooltips: this.config.disableTabTips
29218         });
29219         if(this.config.hideTabs){
29220             ts.stripWrap.setDisplayed(false);
29221         }
29222         this.tabs = ts;
29223         ts.resizeTabs = this.config.resizeTabs === true;
29224         ts.minTabWidth = this.config.minTabWidth || 40;
29225         ts.maxTabWidth = this.config.maxTabWidth || 250;
29226         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29227         ts.monitorResize = false;
29228         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29229         ts.bodyEl.addClass('x-layout-tabs-body');
29230         this.panels.each(this.initPanelAsTab, this);
29231     },
29232
29233     initPanelAsTab : function(panel){
29234         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29235                     this.config.closeOnTab && panel.isClosable());
29236         if(panel.tabTip !== undefined){
29237             ti.setTooltip(panel.tabTip);
29238         }
29239         ti.on("activate", function(){
29240               this.setActivePanel(panel);
29241         }, this);
29242         if(this.config.closeOnTab){
29243             ti.on("beforeclose", function(t, e){
29244                 e.cancel = true;
29245                 this.remove(panel);
29246             }, this);
29247         }
29248         return ti;
29249     },
29250
29251     updatePanelTitle : function(panel, title){
29252         if(this.activePanel == panel){
29253             this.updateTitle(title);
29254         }
29255         if(this.tabs){
29256             var ti = this.tabs.getTab(panel.getEl().id);
29257             ti.setText(title);
29258             if(panel.tabTip !== undefined){
29259                 ti.setTooltip(panel.tabTip);
29260             }
29261         }
29262     },
29263
29264     updateTitle : function(title){
29265         if(this.titleTextEl && !this.config.title){
29266             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29267         }
29268     },
29269
29270     setActivePanel : function(panel){
29271         panel = this.getPanel(panel);
29272         if(this.activePanel && this.activePanel != panel){
29273             this.activePanel.setActiveState(false);
29274         }
29275         this.activePanel = panel;
29276         panel.setActiveState(true);
29277         if(this.panelSize){
29278             panel.setSize(this.panelSize.width, this.panelSize.height);
29279         }
29280         if(this.closeBtn){
29281             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29282         }
29283         this.updateTitle(panel.getTitle());
29284         if(this.tabs){
29285             this.fireEvent("invalidated", this);
29286         }
29287         this.fireEvent("panelactivated", this, panel);
29288     },
29289
29290     /**
29291      * Shows the specified panel.
29292      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29293      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29294      */
29295     showPanel : function(panel){
29296         if(panel = this.getPanel(panel)){
29297             if(this.tabs){
29298                 var tab = this.tabs.getTab(panel.getEl().id);
29299                 if(tab.isHidden()){
29300                     this.tabs.unhideTab(tab.id);
29301                 }
29302                 tab.activate();
29303             }else{
29304                 this.setActivePanel(panel);
29305             }
29306         }
29307         return panel;
29308     },
29309
29310     /**
29311      * Get the active panel for this region.
29312      * @return {Roo.ContentPanel} The active panel or null
29313      */
29314     getActivePanel : function(){
29315         return this.activePanel;
29316     },
29317
29318     validateVisibility : function(){
29319         if(this.panels.getCount() < 1){
29320             this.updateTitle("&#160;");
29321             this.closeBtn.hide();
29322             this.hide();
29323         }else{
29324             if(!this.isVisible()){
29325                 this.show();
29326             }
29327         }
29328     },
29329
29330     /**
29331      * Adds the passed ContentPanel(s) to this region.
29332      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29333      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
29334      */
29335     add : function(panel){
29336         if(arguments.length > 1){
29337             for(var i = 0, len = arguments.length; i < len; i++) {
29338                 this.add(arguments[i]);
29339             }
29340             return null;
29341         }
29342         if(this.hasPanel(panel)){
29343             this.showPanel(panel);
29344             return panel;
29345         }
29346         panel.setRegion(this);
29347         this.panels.add(panel);
29348         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
29349             this.bodyEl.dom.appendChild(panel.getEl().dom);
29350             if(panel.background !== true){
29351                 this.setActivePanel(panel);
29352             }
29353             this.fireEvent("paneladded", this, panel);
29354             return panel;
29355         }
29356         if(!this.tabs){
29357             this.initTabs();
29358         }else{
29359             this.initPanelAsTab(panel);
29360         }
29361         if(panel.background !== true){
29362             this.tabs.activate(panel.getEl().id);
29363         }
29364         this.fireEvent("paneladded", this, panel);
29365         return panel;
29366     },
29367
29368     /**
29369      * Hides the tab for the specified panel.
29370      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29371      */
29372     hidePanel : function(panel){
29373         if(this.tabs && (panel = this.getPanel(panel))){
29374             this.tabs.hideTab(panel.getEl().id);
29375         }
29376     },
29377
29378     /**
29379      * Unhides the tab for a previously hidden panel.
29380      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29381      */
29382     unhidePanel : function(panel){
29383         if(this.tabs && (panel = this.getPanel(panel))){
29384             this.tabs.unhideTab(panel.getEl().id);
29385         }
29386     },
29387
29388     clearPanels : function(){
29389         while(this.panels.getCount() > 0){
29390              this.remove(this.panels.first());
29391         }
29392     },
29393
29394     /**
29395      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29396      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29397      * @param {Boolean} preservePanel Overrides the config preservePanel option
29398      * @return {Roo.ContentPanel} The panel that was removed
29399      */
29400     remove : function(panel, preservePanel){
29401         panel = this.getPanel(panel);
29402         if(!panel){
29403             return null;
29404         }
29405         var e = {};
29406         this.fireEvent("beforeremove", this, panel, e);
29407         if(e.cancel === true){
29408             return null;
29409         }
29410         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
29411         var panelId = panel.getId();
29412         this.panels.removeKey(panelId);
29413         if(preservePanel){
29414             document.body.appendChild(panel.getEl().dom);
29415         }
29416         if(this.tabs){
29417             this.tabs.removeTab(panel.getEl().id);
29418         }else if (!preservePanel){
29419             this.bodyEl.dom.removeChild(panel.getEl().dom);
29420         }
29421         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
29422             var p = this.panels.first();
29423             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
29424             tempEl.appendChild(p.getEl().dom);
29425             this.bodyEl.update("");
29426             this.bodyEl.dom.appendChild(p.getEl().dom);
29427             tempEl = null;
29428             this.updateTitle(p.getTitle());
29429             this.tabs = null;
29430             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29431             this.setActivePanel(p);
29432         }
29433         panel.setRegion(null);
29434         if(this.activePanel == panel){
29435             this.activePanel = null;
29436         }
29437         if(this.config.autoDestroy !== false && preservePanel !== true){
29438             try{panel.destroy();}catch(e){}
29439         }
29440         this.fireEvent("panelremoved", this, panel);
29441         return panel;
29442     },
29443
29444     /**
29445      * Returns the TabPanel component used by this region
29446      * @return {Roo.TabPanel}
29447      */
29448     getTabs : function(){
29449         return this.tabs;
29450     },
29451
29452     createTool : function(parentEl, className){
29453         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
29454             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
29455         btn.addClassOnOver("x-layout-tools-button-over");
29456         return btn;
29457     }
29458 });/*
29459  * Based on:
29460  * Ext JS Library 1.1.1
29461  * Copyright(c) 2006-2007, Ext JS, LLC.
29462  *
29463  * Originally Released Under LGPL - original licence link has changed is not relivant.
29464  *
29465  * Fork - LGPL
29466  * <script type="text/javascript">
29467  */
29468  
29469
29470
29471 /**
29472  * @class Roo.SplitLayoutRegion
29473  * @extends Roo.LayoutRegion
29474  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
29475  */
29476 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
29477     this.cursor = cursor;
29478     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
29479 };
29480
29481 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
29482     splitTip : "Drag to resize.",
29483     collapsibleSplitTip : "Drag to resize. Double click to hide.",
29484     useSplitTips : false,
29485
29486     applyConfig : function(config){
29487         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
29488         if(config.split){
29489             if(!this.split){
29490                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
29491                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
29492                 /** The SplitBar for this region 
29493                 * @type Roo.SplitBar */
29494                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
29495                 this.split.on("moved", this.onSplitMove, this);
29496                 this.split.useShim = config.useShim === true;
29497                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
29498                 if(this.useSplitTips){
29499                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
29500                 }
29501                 if(config.collapsible){
29502                     this.split.el.on("dblclick", this.collapse,  this);
29503                 }
29504             }
29505             if(typeof config.minSize != "undefined"){
29506                 this.split.minSize = config.minSize;
29507             }
29508             if(typeof config.maxSize != "undefined"){
29509                 this.split.maxSize = config.maxSize;
29510             }
29511             if(config.hideWhenEmpty || config.hidden || config.collapsed){
29512                 this.hideSplitter();
29513             }
29514         }
29515     },
29516
29517     getHMaxSize : function(){
29518          var cmax = this.config.maxSize || 10000;
29519          var center = this.mgr.getRegion("center");
29520          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
29521     },
29522
29523     getVMaxSize : function(){
29524          var cmax = this.config.maxSize || 10000;
29525          var center = this.mgr.getRegion("center");
29526          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
29527     },
29528
29529     onSplitMove : function(split, newSize){
29530         this.fireEvent("resized", this, newSize);
29531     },
29532     
29533     /** 
29534      * Returns the {@link Roo.SplitBar} for this region.
29535      * @return {Roo.SplitBar}
29536      */
29537     getSplitBar : function(){
29538         return this.split;
29539     },
29540     
29541     hide : function(){
29542         this.hideSplitter();
29543         Roo.SplitLayoutRegion.superclass.hide.call(this);
29544     },
29545
29546     hideSplitter : function(){
29547         if(this.split){
29548             this.split.el.setLocation(-2000,-2000);
29549             this.split.el.hide();
29550         }
29551     },
29552
29553     show : function(){
29554         if(this.split){
29555             this.split.el.show();
29556         }
29557         Roo.SplitLayoutRegion.superclass.show.call(this);
29558     },
29559     
29560     beforeSlide: function(){
29561         if(Roo.isGecko){// firefox overflow auto bug workaround
29562             this.bodyEl.clip();
29563             if(this.tabs) this.tabs.bodyEl.clip();
29564             if(this.activePanel){
29565                 this.activePanel.getEl().clip();
29566                 
29567                 if(this.activePanel.beforeSlide){
29568                     this.activePanel.beforeSlide();
29569                 }
29570             }
29571         }
29572     },
29573     
29574     afterSlide : function(){
29575         if(Roo.isGecko){// firefox overflow auto bug workaround
29576             this.bodyEl.unclip();
29577             if(this.tabs) this.tabs.bodyEl.unclip();
29578             if(this.activePanel){
29579                 this.activePanel.getEl().unclip();
29580                 if(this.activePanel.afterSlide){
29581                     this.activePanel.afterSlide();
29582                 }
29583             }
29584         }
29585     },
29586
29587     initAutoHide : function(){
29588         if(this.autoHide !== false){
29589             if(!this.autoHideHd){
29590                 var st = new Roo.util.DelayedTask(this.slideIn, this);
29591                 this.autoHideHd = {
29592                     "mouseout": function(e){
29593                         if(!e.within(this.el, true)){
29594                             st.delay(500);
29595                         }
29596                     },
29597                     "mouseover" : function(e){
29598                         st.cancel();
29599                     },
29600                     scope : this
29601                 };
29602             }
29603             this.el.on(this.autoHideHd);
29604         }
29605     },
29606
29607     clearAutoHide : function(){
29608         if(this.autoHide !== false){
29609             this.el.un("mouseout", this.autoHideHd.mouseout);
29610             this.el.un("mouseover", this.autoHideHd.mouseover);
29611         }
29612     },
29613
29614     clearMonitor : function(){
29615         Roo.get(document).un("click", this.slideInIf, this);
29616     },
29617
29618     // these names are backwards but not changed for compat
29619     slideOut : function(){
29620         if(this.isSlid || this.el.hasActiveFx()){
29621             return;
29622         }
29623         this.isSlid = true;
29624         if(this.collapseBtn){
29625             this.collapseBtn.hide();
29626         }
29627         this.closeBtnState = this.closeBtn.getStyle('display');
29628         this.closeBtn.hide();
29629         if(this.stickBtn){
29630             this.stickBtn.show();
29631         }
29632         this.el.show();
29633         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
29634         this.beforeSlide();
29635         this.el.setStyle("z-index", 10001);
29636         this.el.slideIn(this.getSlideAnchor(), {
29637             callback: function(){
29638                 this.afterSlide();
29639                 this.initAutoHide();
29640                 Roo.get(document).on("click", this.slideInIf, this);
29641                 this.fireEvent("slideshow", this);
29642             },
29643             scope: this,
29644             block: true
29645         });
29646     },
29647
29648     afterSlideIn : function(){
29649         this.clearAutoHide();
29650         this.isSlid = false;
29651         this.clearMonitor();
29652         this.el.setStyle("z-index", "");
29653         if(this.collapseBtn){
29654             this.collapseBtn.show();
29655         }
29656         this.closeBtn.setStyle('display', this.closeBtnState);
29657         if(this.stickBtn){
29658             this.stickBtn.hide();
29659         }
29660         this.fireEvent("slidehide", this);
29661     },
29662
29663     slideIn : function(cb){
29664         if(!this.isSlid || this.el.hasActiveFx()){
29665             Roo.callback(cb);
29666             return;
29667         }
29668         this.isSlid = false;
29669         this.beforeSlide();
29670         this.el.slideOut(this.getSlideAnchor(), {
29671             callback: function(){
29672                 this.el.setLeftTop(-10000, -10000);
29673                 this.afterSlide();
29674                 this.afterSlideIn();
29675                 Roo.callback(cb);
29676             },
29677             scope: this,
29678             block: true
29679         });
29680     },
29681     
29682     slideInIf : function(e){
29683         if(!e.within(this.el)){
29684             this.slideIn();
29685         }
29686     },
29687
29688     animateCollapse : function(){
29689         this.beforeSlide();
29690         this.el.setStyle("z-index", 20000);
29691         var anchor = this.getSlideAnchor();
29692         this.el.slideOut(anchor, {
29693             callback : function(){
29694                 this.el.setStyle("z-index", "");
29695                 this.collapsedEl.slideIn(anchor, {duration:.3});
29696                 this.afterSlide();
29697                 this.el.setLocation(-10000,-10000);
29698                 this.el.hide();
29699                 this.fireEvent("collapsed", this);
29700             },
29701             scope: this,
29702             block: true
29703         });
29704     },
29705
29706     animateExpand : function(){
29707         this.beforeSlide();
29708         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
29709         this.el.setStyle("z-index", 20000);
29710         this.collapsedEl.hide({
29711             duration:.1
29712         });
29713         this.el.slideIn(this.getSlideAnchor(), {
29714             callback : function(){
29715                 this.el.setStyle("z-index", "");
29716                 this.afterSlide();
29717                 if(this.split){
29718                     this.split.el.show();
29719                 }
29720                 this.fireEvent("invalidated", this);
29721                 this.fireEvent("expanded", this);
29722             },
29723             scope: this,
29724             block: true
29725         });
29726     },
29727
29728     anchors : {
29729         "west" : "left",
29730         "east" : "right",
29731         "north" : "top",
29732         "south" : "bottom"
29733     },
29734
29735     sanchors : {
29736         "west" : "l",
29737         "east" : "r",
29738         "north" : "t",
29739         "south" : "b"
29740     },
29741
29742     canchors : {
29743         "west" : "tl-tr",
29744         "east" : "tr-tl",
29745         "north" : "tl-bl",
29746         "south" : "bl-tl"
29747     },
29748
29749     getAnchor : function(){
29750         return this.anchors[this.position];
29751     },
29752
29753     getCollapseAnchor : function(){
29754         return this.canchors[this.position];
29755     },
29756
29757     getSlideAnchor : function(){
29758         return this.sanchors[this.position];
29759     },
29760
29761     getAlignAdj : function(){
29762         var cm = this.cmargins;
29763         switch(this.position){
29764             case "west":
29765                 return [0, 0];
29766             break;
29767             case "east":
29768                 return [0, 0];
29769             break;
29770             case "north":
29771                 return [0, 0];
29772             break;
29773             case "south":
29774                 return [0, 0];
29775             break;
29776         }
29777     },
29778
29779     getExpandAdj : function(){
29780         var c = this.collapsedEl, cm = this.cmargins;
29781         switch(this.position){
29782             case "west":
29783                 return [-(cm.right+c.getWidth()+cm.left), 0];
29784             break;
29785             case "east":
29786                 return [cm.right+c.getWidth()+cm.left, 0];
29787             break;
29788             case "north":
29789                 return [0, -(cm.top+cm.bottom+c.getHeight())];
29790             break;
29791             case "south":
29792                 return [0, cm.top+cm.bottom+c.getHeight()];
29793             break;
29794         }
29795     }
29796 });/*
29797  * Based on:
29798  * Ext JS Library 1.1.1
29799  * Copyright(c) 2006-2007, Ext JS, LLC.
29800  *
29801  * Originally Released Under LGPL - original licence link has changed is not relivant.
29802  *
29803  * Fork - LGPL
29804  * <script type="text/javascript">
29805  */
29806 /*
29807  * These classes are private internal classes
29808  */
29809 Roo.CenterLayoutRegion = function(mgr, config){
29810     Roo.LayoutRegion.call(this, mgr, config, "center");
29811     this.visible = true;
29812     this.minWidth = config.minWidth || 20;
29813     this.minHeight = config.minHeight || 20;
29814 };
29815
29816 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
29817     hide : function(){
29818         // center panel can't be hidden
29819     },
29820     
29821     show : function(){
29822         // center panel can't be hidden
29823     },
29824     
29825     getMinWidth: function(){
29826         return this.minWidth;
29827     },
29828     
29829     getMinHeight: function(){
29830         return this.minHeight;
29831     }
29832 });
29833
29834
29835 Roo.NorthLayoutRegion = function(mgr, config){
29836     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
29837     if(this.split){
29838         this.split.placement = Roo.SplitBar.TOP;
29839         this.split.orientation = Roo.SplitBar.VERTICAL;
29840         this.split.el.addClass("x-layout-split-v");
29841     }
29842     var size = config.initialSize || config.height;
29843     if(typeof size != "undefined"){
29844         this.el.setHeight(size);
29845     }
29846 };
29847 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
29848     orientation: Roo.SplitBar.VERTICAL,
29849     getBox : function(){
29850         if(this.collapsed){
29851             return this.collapsedEl.getBox();
29852         }
29853         var box = this.el.getBox();
29854         if(this.split){
29855             box.height += this.split.el.getHeight();
29856         }
29857         return box;
29858     },
29859     
29860     updateBox : function(box){
29861         if(this.split && !this.collapsed){
29862             box.height -= this.split.el.getHeight();
29863             this.split.el.setLeft(box.x);
29864             this.split.el.setTop(box.y+box.height);
29865             this.split.el.setWidth(box.width);
29866         }
29867         if(this.collapsed){
29868             this.updateBody(box.width, null);
29869         }
29870         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29871     }
29872 });
29873
29874 Roo.SouthLayoutRegion = function(mgr, config){
29875     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
29876     if(this.split){
29877         this.split.placement = Roo.SplitBar.BOTTOM;
29878         this.split.orientation = Roo.SplitBar.VERTICAL;
29879         this.split.el.addClass("x-layout-split-v");
29880     }
29881     var size = config.initialSize || config.height;
29882     if(typeof size != "undefined"){
29883         this.el.setHeight(size);
29884     }
29885 };
29886 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
29887     orientation: Roo.SplitBar.VERTICAL,
29888     getBox : function(){
29889         if(this.collapsed){
29890             return this.collapsedEl.getBox();
29891         }
29892         var box = this.el.getBox();
29893         if(this.split){
29894             var sh = this.split.el.getHeight();
29895             box.height += sh;
29896             box.y -= sh;
29897         }
29898         return box;
29899     },
29900     
29901     updateBox : function(box){
29902         if(this.split && !this.collapsed){
29903             var sh = this.split.el.getHeight();
29904             box.height -= sh;
29905             box.y += sh;
29906             this.split.el.setLeft(box.x);
29907             this.split.el.setTop(box.y-sh);
29908             this.split.el.setWidth(box.width);
29909         }
29910         if(this.collapsed){
29911             this.updateBody(box.width, null);
29912         }
29913         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29914     }
29915 });
29916
29917 Roo.EastLayoutRegion = function(mgr, config){
29918     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
29919     if(this.split){
29920         this.split.placement = Roo.SplitBar.RIGHT;
29921         this.split.orientation = Roo.SplitBar.HORIZONTAL;
29922         this.split.el.addClass("x-layout-split-h");
29923     }
29924     var size = config.initialSize || config.width;
29925     if(typeof size != "undefined"){
29926         this.el.setWidth(size);
29927     }
29928 };
29929 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
29930     orientation: Roo.SplitBar.HORIZONTAL,
29931     getBox : function(){
29932         if(this.collapsed){
29933             return this.collapsedEl.getBox();
29934         }
29935         var box = this.el.getBox();
29936         if(this.split){
29937             var sw = this.split.el.getWidth();
29938             box.width += sw;
29939             box.x -= sw;
29940         }
29941         return box;
29942     },
29943
29944     updateBox : function(box){
29945         if(this.split && !this.collapsed){
29946             var sw = this.split.el.getWidth();
29947             box.width -= sw;
29948             this.split.el.setLeft(box.x);
29949             this.split.el.setTop(box.y);
29950             this.split.el.setHeight(box.height);
29951             box.x += sw;
29952         }
29953         if(this.collapsed){
29954             this.updateBody(null, box.height);
29955         }
29956         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29957     }
29958 });
29959
29960 Roo.WestLayoutRegion = function(mgr, config){
29961     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
29962     if(this.split){
29963         this.split.placement = Roo.SplitBar.LEFT;
29964         this.split.orientation = Roo.SplitBar.HORIZONTAL;
29965         this.split.el.addClass("x-layout-split-h");
29966     }
29967     var size = config.initialSize || config.width;
29968     if(typeof size != "undefined"){
29969         this.el.setWidth(size);
29970     }
29971 };
29972 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
29973     orientation: Roo.SplitBar.HORIZONTAL,
29974     getBox : function(){
29975         if(this.collapsed){
29976             return this.collapsedEl.getBox();
29977         }
29978         var box = this.el.getBox();
29979         if(this.split){
29980             box.width += this.split.el.getWidth();
29981         }
29982         return box;
29983     },
29984     
29985     updateBox : function(box){
29986         if(this.split && !this.collapsed){
29987             var sw = this.split.el.getWidth();
29988             box.width -= sw;
29989             this.split.el.setLeft(box.x+box.width);
29990             this.split.el.setTop(box.y);
29991             this.split.el.setHeight(box.height);
29992         }
29993         if(this.collapsed){
29994             this.updateBody(null, box.height);
29995         }
29996         Roo.LayoutRegion.prototype.updateBox.call(this, box);
29997     }
29998 });
29999 /*
30000  * Based on:
30001  * Ext JS Library 1.1.1
30002  * Copyright(c) 2006-2007, Ext JS, LLC.
30003  *
30004  * Originally Released Under LGPL - original licence link has changed is not relivant.
30005  *
30006  * Fork - LGPL
30007  * <script type="text/javascript">
30008  */
30009  
30010  
30011 /*
30012  * Private internal class for reading and applying state
30013  */
30014 Roo.LayoutStateManager = function(layout){
30015      // default empty state
30016      this.state = {
30017         north: {},
30018         south: {},
30019         east: {},
30020         west: {}       
30021     };
30022 };
30023
30024 Roo.LayoutStateManager.prototype = {
30025     init : function(layout, provider){
30026         this.provider = provider;
30027         var state = provider.get(layout.id+"-layout-state");
30028         if(state){
30029             var wasUpdating = layout.isUpdating();
30030             if(!wasUpdating){
30031                 layout.beginUpdate();
30032             }
30033             for(var key in state){
30034                 if(typeof state[key] != "function"){
30035                     var rstate = state[key];
30036                     var r = layout.getRegion(key);
30037                     if(r && rstate){
30038                         if(rstate.size){
30039                             r.resizeTo(rstate.size);
30040                         }
30041                         if(rstate.collapsed == true){
30042                             r.collapse(true);
30043                         }else{
30044                             r.expand(null, true);
30045                         }
30046                     }
30047                 }
30048             }
30049             if(!wasUpdating){
30050                 layout.endUpdate();
30051             }
30052             this.state = state; 
30053         }
30054         this.layout = layout;
30055         layout.on("regionresized", this.onRegionResized, this);
30056         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30057         layout.on("regionexpanded", this.onRegionExpanded, this);
30058     },
30059     
30060     storeState : function(){
30061         this.provider.set(this.layout.id+"-layout-state", this.state);
30062     },
30063     
30064     onRegionResized : function(region, newSize){
30065         this.state[region.getPosition()].size = newSize;
30066         this.storeState();
30067     },
30068     
30069     onRegionCollapsed : function(region){
30070         this.state[region.getPosition()].collapsed = true;
30071         this.storeState();
30072     },
30073     
30074     onRegionExpanded : function(region){
30075         this.state[region.getPosition()].collapsed = false;
30076         this.storeState();
30077     }
30078 };/*
30079  * Based on:
30080  * Ext JS Library 1.1.1
30081  * Copyright(c) 2006-2007, Ext JS, LLC.
30082  *
30083  * Originally Released Under LGPL - original licence link has changed is not relivant.
30084  *
30085  * Fork - LGPL
30086  * <script type="text/javascript">
30087  */
30088 /**
30089  * @class Roo.ContentPanel
30090  * @extends Roo.util.Observable
30091  * A basic ContentPanel element.
30092  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30093  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30094  * @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
30095  * @cfg {Boolean} closable True if the panel can be closed/removed
30096  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30097  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30098  * @cfg {Toolbar} toolbar A toolbar for this panel
30099  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30100  * @cfg {String} title The title for this panel
30101  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30102  * @cfg {String} url Calls {@link #setUrl} with this value
30103  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30104  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30105  * @constructor
30106  * Create a new ContentPanel.
30107  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30108  * @param {String/Object} config A string to set only the title or a config object
30109  * @param {String} content (optional) Set the HTML content for this panel
30110  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30111  */
30112 Roo.ContentPanel = function(el, config, content){
30113     
30114      
30115     /*
30116     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30117         config = el;
30118         el = Roo.id();
30119     }
30120     if (config && config.parentLayout) { 
30121         el = config.parentLayout.el.createChild(); 
30122     }
30123     */
30124     if(el.autoCreate){ // xtype is available if this is called from factory
30125         config = el;
30126         el = Roo.id();
30127     }
30128     this.el = Roo.get(el);
30129     if(!this.el && config && config.autoCreate){
30130         if(typeof config.autoCreate == "object"){
30131             if(!config.autoCreate.id){
30132                 config.autoCreate.id = config.id||el;
30133             }
30134             this.el = Roo.DomHelper.append(document.body,
30135                         config.autoCreate, true);
30136         }else{
30137             this.el = Roo.DomHelper.append(document.body,
30138                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30139         }
30140     }
30141     this.closable = false;
30142     this.loaded = false;
30143     this.active = false;
30144     if(typeof config == "string"){
30145         this.title = config;
30146     }else{
30147         Roo.apply(this, config);
30148     }
30149     
30150     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30151         this.wrapEl = this.el.wrap();    
30152         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30153         
30154     }
30155     
30156     
30157     
30158     if(this.resizeEl){
30159         this.resizeEl = Roo.get(this.resizeEl, true);
30160     }else{
30161         this.resizeEl = this.el;
30162     }
30163     this.addEvents({
30164         /**
30165          * @event activate
30166          * Fires when this panel is activated. 
30167          * @param {Roo.ContentPanel} this
30168          */
30169         "activate" : true,
30170         /**
30171          * @event deactivate
30172          * Fires when this panel is activated. 
30173          * @param {Roo.ContentPanel} this
30174          */
30175         "deactivate" : true,
30176
30177         /**
30178          * @event resize
30179          * Fires when this panel is resized if fitToFrame is true.
30180          * @param {Roo.ContentPanel} this
30181          * @param {Number} width The width after any component adjustments
30182          * @param {Number} height The height after any component adjustments
30183          */
30184         "resize" : true
30185     });
30186     if(this.autoScroll){
30187         this.resizeEl.setStyle("overflow", "auto");
30188     }
30189     content = content || this.content;
30190     if(content){
30191         this.setContent(content);
30192     }
30193     if(config && config.url){
30194         this.setUrl(this.url, this.params, this.loadOnce);
30195     }
30196     
30197     
30198     
30199     Roo.ContentPanel.superclass.constructor.call(this);
30200 };
30201
30202 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30203     tabTip:'',
30204     setRegion : function(region){
30205         this.region = region;
30206         if(region){
30207            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30208         }else{
30209            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30210         } 
30211     },
30212     
30213     /**
30214      * Returns the toolbar for this Panel if one was configured. 
30215      * @return {Roo.Toolbar} 
30216      */
30217     getToolbar : function(){
30218         return this.toolbar;
30219     },
30220     
30221     setActiveState : function(active){
30222         this.active = active;
30223         if(!active){
30224             this.fireEvent("deactivate", this);
30225         }else{
30226             this.fireEvent("activate", this);
30227         }
30228     },
30229     /**
30230      * Updates this panel's element
30231      * @param {String} content The new content
30232      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30233     */
30234     setContent : function(content, loadScripts){
30235         this.el.update(content, loadScripts);
30236     },
30237
30238     ignoreResize : function(w, h){
30239         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30240             return true;
30241         }else{
30242             this.lastSize = {width: w, height: h};
30243             return false;
30244         }
30245     },
30246     /**
30247      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30248      * @return {Roo.UpdateManager} The UpdateManager
30249      */
30250     getUpdateManager : function(){
30251         return this.el.getUpdateManager();
30252     },
30253      /**
30254      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30255      * @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:
30256 <pre><code>
30257 panel.load({
30258     url: "your-url.php",
30259     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30260     callback: yourFunction,
30261     scope: yourObject, //(optional scope)
30262     discardUrl: false,
30263     nocache: false,
30264     text: "Loading...",
30265     timeout: 30,
30266     scripts: false
30267 });
30268 </code></pre>
30269      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30270      * 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.
30271      * @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}
30272      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30273      * @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.
30274      * @return {Roo.ContentPanel} this
30275      */
30276     load : function(){
30277         var um = this.el.getUpdateManager();
30278         um.update.apply(um, arguments);
30279         return this;
30280     },
30281
30282
30283     /**
30284      * 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.
30285      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
30286      * @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)
30287      * @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)
30288      * @return {Roo.UpdateManager} The UpdateManager
30289      */
30290     setUrl : function(url, params, loadOnce){
30291         if(this.refreshDelegate){
30292             this.removeListener("activate", this.refreshDelegate);
30293         }
30294         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30295         this.on("activate", this.refreshDelegate);
30296         return this.el.getUpdateManager();
30297     },
30298     
30299     _handleRefresh : function(url, params, loadOnce){
30300         if(!loadOnce || !this.loaded){
30301             var updater = this.el.getUpdateManager();
30302             updater.update(url, params, this._setLoaded.createDelegate(this));
30303         }
30304     },
30305     
30306     _setLoaded : function(){
30307         this.loaded = true;
30308     }, 
30309     
30310     /**
30311      * Returns this panel's id
30312      * @return {String} 
30313      */
30314     getId : function(){
30315         return this.el.id;
30316     },
30317     
30318     /** 
30319      * Returns this panel's element - used by regiosn to add.
30320      * @return {Roo.Element} 
30321      */
30322     getEl : function(){
30323         return this.wrapEl || this.el;
30324     },
30325     
30326     adjustForComponents : function(width, height){
30327         if(this.resizeEl != this.el){
30328             width -= this.el.getFrameWidth('lr');
30329             height -= this.el.getFrameWidth('tb');
30330         }
30331         if(this.toolbar){
30332             var te = this.toolbar.getEl();
30333             height -= te.getHeight();
30334             te.setWidth(width);
30335         }
30336         if(this.adjustments){
30337             width += this.adjustments[0];
30338             height += this.adjustments[1];
30339         }
30340         return {"width": width, "height": height};
30341     },
30342     
30343     setSize : function(width, height){
30344         if(this.fitToFrame && !this.ignoreResize(width, height)){
30345             if(this.fitContainer && this.resizeEl != this.el){
30346                 this.el.setSize(width, height);
30347             }
30348             var size = this.adjustForComponents(width, height);
30349             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
30350             this.fireEvent('resize', this, size.width, size.height);
30351         }
30352     },
30353     
30354     /**
30355      * Returns this panel's title
30356      * @return {String} 
30357      */
30358     getTitle : function(){
30359         return this.title;
30360     },
30361     
30362     /**
30363      * Set this panel's title
30364      * @param {String} title
30365      */
30366     setTitle : function(title){
30367         this.title = title;
30368         if(this.region){
30369             this.region.updatePanelTitle(this, title);
30370         }
30371     },
30372     
30373     /**
30374      * Returns true is this panel was configured to be closable
30375      * @return {Boolean} 
30376      */
30377     isClosable : function(){
30378         return this.closable;
30379     },
30380     
30381     beforeSlide : function(){
30382         this.el.clip();
30383         this.resizeEl.clip();
30384     },
30385     
30386     afterSlide : function(){
30387         this.el.unclip();
30388         this.resizeEl.unclip();
30389     },
30390     
30391     /**
30392      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
30393      *   Will fail silently if the {@link #setUrl} method has not been called.
30394      *   This does not activate the panel, just updates its content.
30395      */
30396     refresh : function(){
30397         if(this.refreshDelegate){
30398            this.loaded = false;
30399            this.refreshDelegate();
30400         }
30401     },
30402     
30403     /**
30404      * Destroys this panel
30405      */
30406     destroy : function(){
30407         this.el.removeAllListeners();
30408         var tempEl = document.createElement("span");
30409         tempEl.appendChild(this.el.dom);
30410         tempEl.innerHTML = "";
30411         this.el.remove();
30412         this.el = null;
30413     },
30414     
30415       /**
30416      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
30417      * <pre><code>
30418
30419 layout.addxtype({
30420        xtype : 'Form',
30421        items: [ .... ]
30422    }
30423 );
30424
30425 </code></pre>
30426      * @param {Object} cfg Xtype definition of item to add.
30427      */
30428     
30429     addxtype : function(cfg) {
30430         // add form..
30431         if (cfg.xtype.match(/^Form$/)) {
30432             var el = this.el.createChild();
30433
30434             this.form = new  Roo.form.Form(cfg);
30435             
30436             
30437             if ( this.form.allItems.length) this.form.render(el.dom);
30438             return this.form;
30439         }
30440         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
30441             // views..
30442             cfg.el = this.el.appendChild(document.createElement("div"));
30443             // factory?
30444             var ret = new Roo[cfg.xtype](cfg);
30445             ret.render(false, ''); // render blank..
30446             return ret;
30447             
30448         }
30449         return false;
30450         
30451     }
30452 });
30453
30454 /**
30455  * @class Roo.GridPanel
30456  * @extends Roo.ContentPanel
30457  * @constructor
30458  * Create a new GridPanel.
30459  * @param {Roo.grid.Grid} grid The grid for this panel
30460  * @param {String/Object} config A string to set only the panel's title, or a config object
30461  */
30462 Roo.GridPanel = function(grid, config){
30463     
30464   
30465     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
30466         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
30467         
30468     this.wrapper.dom.appendChild(grid.getGridEl().dom);
30469     
30470     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
30471     
30472     if(this.toolbar){
30473         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
30474     }
30475     // xtype created footer. - not sure if will work as we normally have to render first..
30476     if (this.footer && !this.footer.el && this.footer.xtype) {
30477         
30478         this.footer.container = this.grid.getView().getFooterPanel(true);
30479         this.footer.dataSource = this.grid.dataSource;
30480         this.footer = Roo.factory(this.footer, Roo);
30481         
30482     }
30483     
30484     grid.monitorWindowResize = false; // turn off autosizing
30485     grid.autoHeight = false;
30486     grid.autoWidth = false;
30487     this.grid = grid;
30488     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
30489 };
30490
30491 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
30492     getId : function(){
30493         return this.grid.id;
30494     },
30495     
30496     /**
30497      * Returns the grid for this panel
30498      * @return {Roo.grid.Grid} 
30499      */
30500     getGrid : function(){
30501         return this.grid;    
30502     },
30503     
30504     setSize : function(width, height){
30505         if(!this.ignoreResize(width, height)){
30506             var grid = this.grid;
30507             var size = this.adjustForComponents(width, height);
30508             grid.getGridEl().setSize(size.width, size.height);
30509             grid.autoSize();
30510         }
30511     },
30512     
30513     beforeSlide : function(){
30514         this.grid.getView().scroller.clip();
30515     },
30516     
30517     afterSlide : function(){
30518         this.grid.getView().scroller.unclip();
30519     },
30520     
30521     destroy : function(){
30522         this.grid.destroy();
30523         delete this.grid;
30524         Roo.GridPanel.superclass.destroy.call(this); 
30525     }
30526 });
30527
30528
30529 /**
30530  * @class Roo.NestedLayoutPanel
30531  * @extends Roo.ContentPanel
30532  * @constructor
30533  * Create a new NestedLayoutPanel.
30534  * 
30535  * 
30536  * @param {Roo.BorderLayout} layout The layout for this panel
30537  * @param {String/Object} config A string to set only the title or a config object
30538  */
30539 Roo.NestedLayoutPanel = function(layout, config)
30540 {
30541     // construct with only one argument..
30542     /* FIXME - implement nicer consturctors
30543     if (layout.layout) {
30544         config = layout;
30545         layout = config.layout;
30546         delete config.layout;
30547     }
30548     if (layout.xtype && !layout.getEl) {
30549         // then layout needs constructing..
30550         layout = Roo.factory(layout, Roo);
30551     }
30552     */
30553     
30554     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
30555     
30556     layout.monitorWindowResize = false; // turn off autosizing
30557     this.layout = layout;
30558     this.layout.getEl().addClass("x-layout-nested-layout");
30559     
30560     
30561     
30562 };
30563
30564 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
30565
30566     setSize : function(width, height){
30567         if(!this.ignoreResize(width, height)){
30568             var size = this.adjustForComponents(width, height);
30569             var el = this.layout.getEl();
30570             el.setSize(size.width, size.height);
30571             var touch = el.dom.offsetWidth;
30572             this.layout.layout();
30573             // ie requires a double layout on the first pass
30574             if(Roo.isIE && !this.initialized){
30575                 this.initialized = true;
30576                 this.layout.layout();
30577             }
30578         }
30579     },
30580     
30581     // activate all subpanels if not currently active..
30582     
30583     setActiveState : function(active){
30584         this.active = active;
30585         if(!active){
30586             this.fireEvent("deactivate", this);
30587             return;
30588         }
30589         
30590         this.fireEvent("activate", this);
30591         // not sure if this should happen before or after..
30592         if (!this.layout) {
30593             return; // should not happen..
30594         }
30595         var reg = false;
30596         for (var r in this.layout.regions) {
30597             reg = this.layout.getRegion(r);
30598             if (reg.getActivePanel()) {
30599                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
30600                 reg.setActivePanel(reg.getActivePanel());
30601                 continue;
30602             }
30603             if (!reg.panels.length) {
30604                 continue;
30605             }
30606             reg.showPanel(reg.getPanel(0));
30607         }
30608         
30609         
30610         
30611         
30612     },
30613     
30614     /**
30615      * Returns the nested BorderLayout for this panel
30616      * @return {Roo.BorderLayout} 
30617      */
30618     getLayout : function(){
30619         return this.layout;
30620     },
30621     
30622      /**
30623      * Adds a xtype elements to the layout of the nested panel
30624      * <pre><code>
30625
30626 panel.addxtype({
30627        xtype : 'ContentPanel',
30628        region: 'west',
30629        items: [ .... ]
30630    }
30631 );
30632
30633 panel.addxtype({
30634         xtype : 'NestedLayoutPanel',
30635         region: 'west',
30636         layout: {
30637            center: { },
30638            west: { }   
30639         },
30640         items : [ ... list of content panels or nested layout panels.. ]
30641    }
30642 );
30643 </code></pre>
30644      * @param {Object} cfg Xtype definition of item to add.
30645      */
30646     addxtype : function(cfg) {
30647         return this.layout.addxtype(cfg);
30648     
30649     }
30650 });
30651
30652 Roo.ScrollPanel = function(el, config, content){
30653     config = config || {};
30654     config.fitToFrame = true;
30655     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
30656     
30657     this.el.dom.style.overflow = "hidden";
30658     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
30659     this.el.removeClass("x-layout-inactive-content");
30660     this.el.on("mousewheel", this.onWheel, this);
30661
30662     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
30663     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
30664     up.unselectable(); down.unselectable();
30665     up.on("click", this.scrollUp, this);
30666     down.on("click", this.scrollDown, this);
30667     up.addClassOnOver("x-scroller-btn-over");
30668     down.addClassOnOver("x-scroller-btn-over");
30669     up.addClassOnClick("x-scroller-btn-click");
30670     down.addClassOnClick("x-scroller-btn-click");
30671     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
30672
30673     this.resizeEl = this.el;
30674     this.el = wrap; this.up = up; this.down = down;
30675 };
30676
30677 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
30678     increment : 100,
30679     wheelIncrement : 5,
30680     scrollUp : function(){
30681         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
30682     },
30683
30684     scrollDown : function(){
30685         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
30686     },
30687
30688     afterScroll : function(){
30689         var el = this.resizeEl;
30690         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
30691         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
30692         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
30693     },
30694
30695     setSize : function(){
30696         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
30697         this.afterScroll();
30698     },
30699
30700     onWheel : function(e){
30701         var d = e.getWheelDelta();
30702         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
30703         this.afterScroll();
30704         e.stopEvent();
30705     },
30706
30707     setContent : function(content, loadScripts){
30708         this.resizeEl.update(content, loadScripts);
30709     }
30710
30711 });
30712
30713
30714
30715
30716
30717
30718
30719
30720
30721 /**
30722  * @class Roo.TreePanel
30723  * @extends Roo.ContentPanel
30724  * @constructor
30725  * Create a new TreePanel.
30726  * @param {String/Object} config A string to set only the panel's title, or a config object
30727  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
30728  */
30729 Roo.TreePanel = function(config){
30730     var el = config.el;
30731     var tree = config.tree;
30732     delete config.tree; 
30733     delete config.el; // hopefull!
30734     Roo.TreePanel.superclass.constructor.call(this, el, config);
30735     var treeEl = el.createChild();
30736     this.tree = new Roo.tree.TreePanel(treeEl , tree);
30737     //console.log(tree);
30738     this.on('activate', function()
30739     {
30740         if (this.tree.rendered) {
30741             return;
30742         }
30743         //console.log('render tree');
30744         this.tree.render();
30745     });
30746     
30747     this.on('resize',  function (cp, w, h) {
30748             this.tree.innerCt.setWidth(w);
30749             this.tree.innerCt.setHeight(h);
30750             this.tree.innerCt.setStyle('overflow-y', 'auto');
30751     });
30752
30753         
30754     
30755 };
30756
30757 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
30758
30759
30760
30761
30762
30763
30764
30765
30766
30767
30768
30769 /*
30770  * Based on:
30771  * Ext JS Library 1.1.1
30772  * Copyright(c) 2006-2007, Ext JS, LLC.
30773  *
30774  * Originally Released Under LGPL - original licence link has changed is not relivant.
30775  *
30776  * Fork - LGPL
30777  * <script type="text/javascript">
30778  */
30779  
30780
30781 /**
30782  * @class Roo.ReaderLayout
30783  * @extends Roo.BorderLayout
30784  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
30785  * center region containing two nested regions (a top one for a list view and one for item preview below),
30786  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
30787  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
30788  * expedites the setup of the overall layout and regions for this common application style.
30789  * Example:
30790  <pre><code>
30791 var reader = new Roo.ReaderLayout();
30792 var CP = Roo.ContentPanel;  // shortcut for adding
30793
30794 reader.beginUpdate();
30795 reader.add("north", new CP("north", "North"));
30796 reader.add("west", new CP("west", {title: "West"}));
30797 reader.add("east", new CP("east", {title: "East"}));
30798
30799 reader.regions.listView.add(new CP("listView", "List"));
30800 reader.regions.preview.add(new CP("preview", "Preview"));
30801 reader.endUpdate();
30802 </code></pre>
30803 * @constructor
30804 * Create a new ReaderLayout
30805 * @param {Object} config Configuration options
30806 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
30807 * document.body if omitted)
30808 */
30809 Roo.ReaderLayout = function(config, renderTo){
30810     var c = config || {size:{}};
30811     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
30812         north: c.north !== false ? Roo.apply({
30813             split:false,
30814             initialSize: 32,
30815             titlebar: false
30816         }, c.north) : false,
30817         west: c.west !== false ? Roo.apply({
30818             split:true,
30819             initialSize: 200,
30820             minSize: 175,
30821             maxSize: 400,
30822             titlebar: true,
30823             collapsible: true,
30824             animate: true,
30825             margins:{left:5,right:0,bottom:5,top:5},
30826             cmargins:{left:5,right:5,bottom:5,top:5}
30827         }, c.west) : false,
30828         east: c.east !== false ? Roo.apply({
30829             split:true,
30830             initialSize: 200,
30831             minSize: 175,
30832             maxSize: 400,
30833             titlebar: true,
30834             collapsible: true,
30835             animate: true,
30836             margins:{left:0,right:5,bottom:5,top:5},
30837             cmargins:{left:5,right:5,bottom:5,top:5}
30838         }, c.east) : false,
30839         center: Roo.apply({
30840             tabPosition: 'top',
30841             autoScroll:false,
30842             closeOnTab: true,
30843             titlebar:false,
30844             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
30845         }, c.center)
30846     });
30847
30848     this.el.addClass('x-reader');
30849
30850     this.beginUpdate();
30851
30852     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
30853         south: c.preview !== false ? Roo.apply({
30854             split:true,
30855             initialSize: 200,
30856             minSize: 100,
30857             autoScroll:true,
30858             collapsible:true,
30859             titlebar: true,
30860             cmargins:{top:5,left:0, right:0, bottom:0}
30861         }, c.preview) : false,
30862         center: Roo.apply({
30863             autoScroll:false,
30864             titlebar:false,
30865             minHeight:200
30866         }, c.listView)
30867     });
30868     this.add('center', new Roo.NestedLayoutPanel(inner,
30869             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
30870
30871     this.endUpdate();
30872
30873     this.regions.preview = inner.getRegion('south');
30874     this.regions.listView = inner.getRegion('center');
30875 };
30876
30877 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
30878  * Based on:
30879  * Ext JS Library 1.1.1
30880  * Copyright(c) 2006-2007, Ext JS, LLC.
30881  *
30882  * Originally Released Under LGPL - original licence link has changed is not relivant.
30883  *
30884  * Fork - LGPL
30885  * <script type="text/javascript">
30886  */
30887  
30888 /**
30889  * @class Roo.grid.Grid
30890  * @extends Roo.util.Observable
30891  * This class represents the primary interface of a component based grid control.
30892  * <br><br>Usage:<pre><code>
30893  var grid = new Roo.grid.Grid("my-container-id", {
30894      ds: myDataStore,
30895      cm: myColModel,
30896      selModel: mySelectionModel,
30897      autoSizeColumns: true,
30898      monitorWindowResize: false,
30899      trackMouseOver: true
30900  });
30901  // set any options
30902  grid.render();
30903  * </code></pre>
30904  * <b>Common Problems:</b><br/>
30905  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
30906  * element will correct this<br/>
30907  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
30908  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
30909  * are unpredictable.<br/>
30910  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
30911  * grid to calculate dimensions/offsets.<br/>
30912   * @constructor
30913  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
30914  * The container MUST have some type of size defined for the grid to fill. The container will be
30915  * automatically set to position relative if it isn't already.
30916  * @param {Object} config A config object that sets properties on this grid.
30917  */
30918 Roo.grid.Grid = function(container, config){
30919         // initialize the container
30920         this.container = Roo.get(container);
30921         this.container.update("");
30922         this.container.setStyle("overflow", "hidden");
30923     this.container.addClass('x-grid-container');
30924
30925     this.id = this.container.id;
30926
30927     Roo.apply(this, config);
30928     // check and correct shorthanded configs
30929     if(this.ds){
30930         this.dataSource = this.ds;
30931         delete this.ds;
30932     }
30933     if(this.cm){
30934         this.colModel = this.cm;
30935         delete this.cm;
30936     }
30937     if(this.sm){
30938         this.selModel = this.sm;
30939         delete this.sm;
30940     }
30941
30942     if (this.selModel) {
30943         this.selModel = Roo.factory(this.selModel, Roo.grid);
30944         this.sm = this.selModel;
30945         this.sm.xmodule = this.xmodule || false;
30946     }
30947     if (typeof(this.colModel.config) == 'undefined') {
30948         this.colModel = new Roo.grid.ColumnModel(this.colModel);
30949         this.cm = this.colModel;
30950         this.cm.xmodule = this.xmodule || false;
30951     }
30952     if (this.dataSource) {
30953         this.dataSource= Roo.factory(this.dataSource, Roo.data);
30954         this.ds = this.dataSource;
30955         this.ds.xmodule = this.xmodule || false;
30956         
30957     }
30958     
30959     
30960     
30961     if(this.width){
30962         this.container.setWidth(this.width);
30963     }
30964
30965     if(this.height){
30966         this.container.setHeight(this.height);
30967     }
30968     /** @private */
30969         this.addEvents({
30970             // raw events
30971             /**
30972              * @event click
30973              * The raw click event for the entire grid.
30974              * @param {Roo.EventObject} e
30975              */
30976             "click" : true,
30977             /**
30978              * @event dblclick
30979              * The raw dblclick event for the entire grid.
30980              * @param {Roo.EventObject} e
30981              */
30982             "dblclick" : true,
30983             /**
30984              * @event contextmenu
30985              * The raw contextmenu event for the entire grid.
30986              * @param {Roo.EventObject} e
30987              */
30988             "contextmenu" : true,
30989             /**
30990              * @event mousedown
30991              * The raw mousedown event for the entire grid.
30992              * @param {Roo.EventObject} e
30993              */
30994             "mousedown" : true,
30995             /**
30996              * @event mouseup
30997              * The raw mouseup event for the entire grid.
30998              * @param {Roo.EventObject} e
30999              */
31000             "mouseup" : true,
31001             /**
31002              * @event mouseover
31003              * The raw mouseover event for the entire grid.
31004              * @param {Roo.EventObject} e
31005              */
31006             "mouseover" : true,
31007             /**
31008              * @event mouseout
31009              * The raw mouseout event for the entire grid.
31010              * @param {Roo.EventObject} e
31011              */
31012             "mouseout" : true,
31013             /**
31014              * @event keypress
31015              * The raw keypress event for the entire grid.
31016              * @param {Roo.EventObject} e
31017              */
31018             "keypress" : true,
31019             /**
31020              * @event keydown
31021              * The raw keydown event for the entire grid.
31022              * @param {Roo.EventObject} e
31023              */
31024             "keydown" : true,
31025
31026             // custom events
31027
31028             /**
31029              * @event cellclick
31030              * Fires when a cell is clicked
31031              * @param {Grid} this
31032              * @param {Number} rowIndex
31033              * @param {Number} columnIndex
31034              * @param {Roo.EventObject} e
31035              */
31036             "cellclick" : true,
31037             /**
31038              * @event celldblclick
31039              * Fires when a cell is double clicked
31040              * @param {Grid} this
31041              * @param {Number} rowIndex
31042              * @param {Number} columnIndex
31043              * @param {Roo.EventObject} e
31044              */
31045             "celldblclick" : true,
31046             /**
31047              * @event rowclick
31048              * Fires when a row is clicked
31049              * @param {Grid} this
31050              * @param {Number} rowIndex
31051              * @param {Roo.EventObject} e
31052              */
31053             "rowclick" : true,
31054             /**
31055              * @event rowdblclick
31056              * Fires when a row is double clicked
31057              * @param {Grid} this
31058              * @param {Number} rowIndex
31059              * @param {Roo.EventObject} e
31060              */
31061             "rowdblclick" : true,
31062             /**
31063              * @event headerclick
31064              * Fires when a header is clicked
31065              * @param {Grid} this
31066              * @param {Number} columnIndex
31067              * @param {Roo.EventObject} e
31068              */
31069             "headerclick" : true,
31070             /**
31071              * @event headerdblclick
31072              * Fires when a header cell is double clicked
31073              * @param {Grid} this
31074              * @param {Number} columnIndex
31075              * @param {Roo.EventObject} e
31076              */
31077             "headerdblclick" : true,
31078             /**
31079              * @event rowcontextmenu
31080              * Fires when a row is right clicked
31081              * @param {Grid} this
31082              * @param {Number} rowIndex
31083              * @param {Roo.EventObject} e
31084              */
31085             "rowcontextmenu" : true,
31086             /**
31087          * @event cellcontextmenu
31088          * Fires when a cell is right clicked
31089          * @param {Grid} this
31090          * @param {Number} rowIndex
31091          * @param {Number} cellIndex
31092          * @param {Roo.EventObject} e
31093          */
31094          "cellcontextmenu" : true,
31095             /**
31096              * @event headercontextmenu
31097              * Fires when a header is right clicked
31098              * @param {Grid} this
31099              * @param {Number} columnIndex
31100              * @param {Roo.EventObject} e
31101              */
31102             "headercontextmenu" : true,
31103             /**
31104              * @event bodyscroll
31105              * Fires when the body element is scrolled
31106              * @param {Number} scrollLeft
31107              * @param {Number} scrollTop
31108              */
31109             "bodyscroll" : true,
31110             /**
31111              * @event columnresize
31112              * Fires when the user resizes a column
31113              * @param {Number} columnIndex
31114              * @param {Number} newSize
31115              */
31116             "columnresize" : true,
31117             /**
31118              * @event columnmove
31119              * Fires when the user moves a column
31120              * @param {Number} oldIndex
31121              * @param {Number} newIndex
31122              */
31123             "columnmove" : true,
31124             /**
31125              * @event startdrag
31126              * Fires when row(s) start being dragged
31127              * @param {Grid} this
31128              * @param {Roo.GridDD} dd The drag drop object
31129              * @param {event} e The raw browser event
31130              */
31131             "startdrag" : true,
31132             /**
31133              * @event enddrag
31134              * Fires when a drag operation is complete
31135              * @param {Grid} this
31136              * @param {Roo.GridDD} dd The drag drop object
31137              * @param {event} e The raw browser event
31138              */
31139             "enddrag" : true,
31140             /**
31141              * @event dragdrop
31142              * Fires when dragged row(s) are dropped on a valid DD target
31143              * @param {Grid} this
31144              * @param {Roo.GridDD} dd The drag drop object
31145              * @param {String} targetId The target drag drop object
31146              * @param {event} e The raw browser event
31147              */
31148             "dragdrop" : true,
31149             /**
31150              * @event dragover
31151              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31152              * @param {Grid} this
31153              * @param {Roo.GridDD} dd The drag drop object
31154              * @param {String} targetId The target drag drop object
31155              * @param {event} e The raw browser event
31156              */
31157             "dragover" : true,
31158             /**
31159              * @event dragenter
31160              *  Fires when the dragged row(s) first cross another DD target while being dragged
31161              * @param {Grid} this
31162              * @param {Roo.GridDD} dd The drag drop object
31163              * @param {String} targetId The target drag drop object
31164              * @param {event} e The raw browser event
31165              */
31166             "dragenter" : true,
31167             /**
31168              * @event dragout
31169              * Fires when the dragged row(s) leave another DD target while being dragged
31170              * @param {Grid} this
31171              * @param {Roo.GridDD} dd The drag drop object
31172              * @param {String} targetId The target drag drop object
31173              * @param {event} e The raw browser event
31174              */
31175             "dragout" : true,
31176         /**
31177          * @event render
31178          * Fires when the grid is rendered
31179          * @param {Grid} grid
31180          */
31181         render : true
31182     });
31183
31184     Roo.grid.Grid.superclass.constructor.call(this);
31185 };
31186 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31187     /**
31188      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31189          */
31190         minColumnWidth : 25,
31191
31192     /**
31193          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31194          * <b>on initial render.</b> It is more efficient to explicitly size the columns
31195          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31196          */
31197         autoSizeColumns : false,
31198
31199         /**
31200          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31201          */
31202         autoSizeHeaders : true,
31203
31204         /**
31205          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31206          */
31207         monitorWindowResize : true,
31208
31209         /**
31210          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31211          * rows measured to get a columns size. Default is 0 (all rows).
31212          */
31213         maxRowsToMeasure : 0,
31214
31215         /**
31216          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31217          */
31218         trackMouseOver : true,
31219
31220         /**
31221          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
31222          */
31223         enableDragDrop : false,
31224
31225         /**
31226          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
31227          */
31228         enableColumnMove : true,
31229
31230         /**
31231          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
31232          */
31233         enableColumnHide : true,
31234
31235         /**
31236          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
31237          */
31238         enableRowHeightSync : false,
31239
31240         /**
31241          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
31242          */
31243         stripeRows : true,
31244
31245         /**
31246          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
31247          */
31248         autoHeight : false,
31249
31250     /**
31251      * @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.
31252      */
31253     autoExpandColumn : false,
31254
31255     /**
31256     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
31257     * Default is 50.
31258     */
31259     autoExpandMin : 50,
31260
31261     /**
31262     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
31263     */
31264     autoExpandMax : 1000,
31265
31266     /**
31267          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
31268          */
31269         view : null,
31270
31271         /**
31272      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
31273          */
31274         loadMask : false,
31275
31276     // private
31277     rendered : false,
31278
31279     /**
31280     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
31281     * of a fixed width. Default is false.
31282     */
31283     /**
31284     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
31285     */
31286     /**
31287      * Called once after all setup has been completed and the grid is ready to be rendered.
31288      * @return {Roo.grid.Grid} this
31289      */
31290     render : function(){
31291         var c = this.container;
31292         // try to detect autoHeight/width mode
31293         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
31294             this.autoHeight = true;
31295         }
31296         var view = this.getView();
31297         view.init(this);
31298
31299         c.on("click", this.onClick, this);
31300         c.on("dblclick", this.onDblClick, this);
31301         c.on("contextmenu", this.onContextMenu, this);
31302         c.on("keydown", this.onKeyDown, this);
31303
31304         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
31305
31306         this.getSelectionModel().init(this);
31307
31308         view.render();
31309
31310         if(this.loadMask){
31311             this.loadMask = new Roo.LoadMask(this.container,
31312                     Roo.apply({store:this.dataSource}, this.loadMask));
31313         }
31314         
31315         
31316         if (this.toolbar && this.toolbar.xtype) {
31317             this.toolbar.container = this.getView().getHeaderPanel(true);
31318             this.toolbar = new Ext.Toolbar(this.toolbar);
31319         }
31320         if (this.footer && this.footer.xtype) {
31321             this.footer.dataSource = this.getDataSource();
31322             this.footer.container = this.getView().getFooterPanel(true);
31323             this.footer = Roo.factory(this.footer, Roo);
31324         }
31325         this.rendered = true;
31326         this.fireEvent('render', this);
31327         return this;
31328     },
31329
31330         /**
31331          * Reconfigures the grid to use a different Store and Column Model.
31332          * The View will be bound to the new objects and refreshed.
31333          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
31334          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
31335          */
31336     reconfigure : function(dataSource, colModel){
31337         if(this.loadMask){
31338             this.loadMask.destroy();
31339             this.loadMask = new Roo.LoadMask(this.container,
31340                     Roo.apply({store:dataSource}, this.loadMask));
31341         }
31342         this.view.bind(dataSource, colModel);
31343         this.dataSource = dataSource;
31344         this.colModel = colModel;
31345         this.view.refresh(true);
31346     },
31347
31348     // private
31349     onKeyDown : function(e){
31350         this.fireEvent("keydown", e);
31351     },
31352
31353     /**
31354      * Destroy this grid.
31355      * @param {Boolean} removeEl True to remove the element
31356      */
31357     destroy : function(removeEl, keepListeners){
31358         if(this.loadMask){
31359             this.loadMask.destroy();
31360         }
31361         var c = this.container;
31362         c.removeAllListeners();
31363         this.view.destroy();
31364         this.colModel.purgeListeners();
31365         if(!keepListeners){
31366             this.purgeListeners();
31367         }
31368         c.update("");
31369         if(removeEl === true){
31370             c.remove();
31371         }
31372     },
31373
31374     // private
31375     processEvent : function(name, e){
31376         this.fireEvent(name, e);
31377         var t = e.getTarget();
31378         var v = this.view;
31379         var header = v.findHeaderIndex(t);
31380         if(header !== false){
31381             this.fireEvent("header" + name, this, header, e);
31382         }else{
31383             var row = v.findRowIndex(t);
31384             var cell = v.findCellIndex(t);
31385             if(row !== false){
31386                 this.fireEvent("row" + name, this, row, e);
31387                 if(cell !== false){
31388                     this.fireEvent("cell" + name, this, row, cell, e);
31389                 }
31390             }
31391         }
31392     },
31393
31394     // private
31395     onClick : function(e){
31396         this.processEvent("click", e);
31397     },
31398
31399     // private
31400     onContextMenu : function(e, t){
31401         this.processEvent("contextmenu", e);
31402     },
31403
31404     // private
31405     onDblClick : function(e){
31406         this.processEvent("dblclick", e);
31407     },
31408
31409     // private
31410     walkCells : function(row, col, step, fn, scope){
31411         var cm = this.colModel, clen = cm.getColumnCount();
31412         var ds = this.dataSource, rlen = ds.getCount(), first = true;
31413         if(step < 0){
31414             if(col < 0){
31415                 row--;
31416                 first = false;
31417             }
31418             while(row >= 0){
31419                 if(!first){
31420                     col = clen-1;
31421                 }
31422                 first = false;
31423                 while(col >= 0){
31424                     if(fn.call(scope || this, row, col, cm) === true){
31425                         return [row, col];
31426                     }
31427                     col--;
31428                 }
31429                 row--;
31430             }
31431         } else {
31432             if(col >= clen){
31433                 row++;
31434                 first = false;
31435             }
31436             while(row < rlen){
31437                 if(!first){
31438                     col = 0;
31439                 }
31440                 first = false;
31441                 while(col < clen){
31442                     if(fn.call(scope || this, row, col, cm) === true){
31443                         return [row, col];
31444                     }
31445                     col++;
31446                 }
31447                 row++;
31448             }
31449         }
31450         return null;
31451     },
31452
31453     // private
31454     getSelections : function(){
31455         return this.selModel.getSelections();
31456     },
31457
31458     /**
31459      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
31460      * but if manual update is required this method will initiate it.
31461      */
31462     autoSize : function(){
31463         if(this.rendered){
31464             this.view.layout();
31465             if(this.view.adjustForScroll){
31466                 this.view.adjustForScroll();
31467             }
31468         }
31469     },
31470
31471     /**
31472      * Returns the grid's underlying element.
31473      * @return {Element} The element
31474      */
31475     getGridEl : function(){
31476         return this.container;
31477     },
31478
31479     // private for compatibility, overridden by editor grid
31480     stopEditing : function(){},
31481
31482     /**
31483      * Returns the grid's SelectionModel.
31484      * @return {SelectionModel}
31485      */
31486     getSelectionModel : function(){
31487         if(!this.selModel){
31488             this.selModel = new Roo.grid.RowSelectionModel();
31489         }
31490         return this.selModel;
31491     },
31492
31493     /**
31494      * Returns the grid's DataSource.
31495      * @return {DataSource}
31496      */
31497     getDataSource : function(){
31498         return this.dataSource;
31499     },
31500
31501     /**
31502      * Returns the grid's ColumnModel.
31503      * @return {ColumnModel}
31504      */
31505     getColumnModel : function(){
31506         return this.colModel;
31507     },
31508
31509     /**
31510      * Returns the grid's GridView object.
31511      * @return {GridView}
31512      */
31513     getView : function(){
31514         if(!this.view){
31515             this.view = new Roo.grid.GridView(this.viewConfig);
31516         }
31517         return this.view;
31518     },
31519     /**
31520      * Called to get grid's drag proxy text, by default returns this.ddText.
31521      * @return {String}
31522      */
31523     getDragDropText : function(){
31524         var count = this.selModel.getCount();
31525         return String.format(this.ddText, count, count == 1 ? '' : 's');
31526     }
31527 });
31528 /**
31529  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
31530  * %0 is replaced with the number of selected rows.
31531  * @type String
31532  */
31533 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
31534  * Based on:
31535  * Ext JS Library 1.1.1
31536  * Copyright(c) 2006-2007, Ext JS, LLC.
31537  *
31538  * Originally Released Under LGPL - original licence link has changed is not relivant.
31539  *
31540  * Fork - LGPL
31541  * <script type="text/javascript">
31542  */
31543  
31544 Roo.grid.AbstractGridView = function(){
31545         this.grid = null;
31546         
31547         this.events = {
31548             "beforerowremoved" : true,
31549             "beforerowsinserted" : true,
31550             "beforerefresh" : true,
31551             "rowremoved" : true,
31552             "rowsinserted" : true,
31553             "rowupdated" : true,
31554             "refresh" : true
31555         };
31556     Roo.grid.AbstractGridView.superclass.constructor.call(this);
31557 };
31558
31559 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
31560     rowClass : "x-grid-row",
31561     cellClass : "x-grid-cell",
31562     tdClass : "x-grid-td",
31563     hdClass : "x-grid-hd",
31564     splitClass : "x-grid-hd-split",
31565     
31566         init: function(grid){
31567         this.grid = grid;
31568                 var cid = this.grid.getGridEl().id;
31569         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
31570         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
31571         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
31572         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
31573         },
31574         
31575         getColumnRenderers : function(){
31576         var renderers = [];
31577         var cm = this.grid.colModel;
31578         var colCount = cm.getColumnCount();
31579         for(var i = 0; i < colCount; i++){
31580             renderers[i] = cm.getRenderer(i);
31581         }
31582         return renderers;
31583     },
31584     
31585     getColumnIds : function(){
31586         var ids = [];
31587         var cm = this.grid.colModel;
31588         var colCount = cm.getColumnCount();
31589         for(var i = 0; i < colCount; i++){
31590             ids[i] = cm.getColumnId(i);
31591         }
31592         return ids;
31593     },
31594     
31595     getDataIndexes : function(){
31596         if(!this.indexMap){
31597             this.indexMap = this.buildIndexMap();
31598         }
31599         return this.indexMap.colToData;
31600     },
31601     
31602     getColumnIndexByDataIndex : function(dataIndex){
31603         if(!this.indexMap){
31604             this.indexMap = this.buildIndexMap();
31605         }
31606         return this.indexMap.dataToCol[dataIndex];
31607     },
31608     
31609     /**
31610      * Set a css style for a column dynamically. 
31611      * @param {Number} colIndex The index of the column
31612      * @param {String} name The css property name
31613      * @param {String} value The css value
31614      */
31615     setCSSStyle : function(colIndex, name, value){
31616         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
31617         Roo.util.CSS.updateRule(selector, name, value);
31618     },
31619     
31620     generateRules : function(cm){
31621         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
31622         Roo.util.CSS.removeStyleSheet(rulesId);
31623         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
31624             var cid = cm.getColumnId(i);
31625             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
31626                          this.tdSelector, cid, " {\n}\n",
31627                          this.hdSelector, cid, " {\n}\n",
31628                          this.splitSelector, cid, " {\n}\n");
31629         }
31630         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
31631     }
31632 });/*
31633  * Based on:
31634  * Ext JS Library 1.1.1
31635  * Copyright(c) 2006-2007, Ext JS, LLC.
31636  *
31637  * Originally Released Under LGPL - original licence link has changed is not relivant.
31638  *
31639  * Fork - LGPL
31640  * <script type="text/javascript">
31641  */
31642
31643 // private
31644 // This is a support class used internally by the Grid components
31645 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
31646     this.grid = grid;
31647     this.view = grid.getView();
31648     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
31649     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
31650     if(hd2){
31651         this.setHandleElId(Roo.id(hd));
31652         this.setOuterHandleElId(Roo.id(hd2));
31653     }
31654     this.scroll = false;
31655 };
31656 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
31657     maxDragWidth: 120,
31658     getDragData : function(e){
31659         var t = Roo.lib.Event.getTarget(e);
31660         var h = this.view.findHeaderCell(t);
31661         if(h){
31662             return {ddel: h.firstChild, header:h};
31663         }
31664         return false;
31665     },
31666
31667     onInitDrag : function(e){
31668         this.view.headersDisabled = true;
31669         var clone = this.dragData.ddel.cloneNode(true);
31670         clone.id = Roo.id();
31671         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
31672         this.proxy.update(clone);
31673         return true;
31674     },
31675
31676     afterValidDrop : function(){
31677         var v = this.view;
31678         setTimeout(function(){
31679             v.headersDisabled = false;
31680         }, 50);
31681     },
31682
31683     afterInvalidDrop : function(){
31684         var v = this.view;
31685         setTimeout(function(){
31686             v.headersDisabled = false;
31687         }, 50);
31688     }
31689 });
31690 /*
31691  * Based on:
31692  * Ext JS Library 1.1.1
31693  * Copyright(c) 2006-2007, Ext JS, LLC.
31694  *
31695  * Originally Released Under LGPL - original licence link has changed is not relivant.
31696  *
31697  * Fork - LGPL
31698  * <script type="text/javascript">
31699  */
31700 // private
31701 // This is a support class used internally by the Grid components
31702 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
31703     this.grid = grid;
31704     this.view = grid.getView();
31705     // split the proxies so they don't interfere with mouse events
31706     this.proxyTop = Roo.DomHelper.append(document.body, {
31707         cls:"col-move-top", html:"&#160;"
31708     }, true);
31709     this.proxyBottom = Roo.DomHelper.append(document.body, {
31710         cls:"col-move-bottom", html:"&#160;"
31711     }, true);
31712     this.proxyTop.hide = this.proxyBottom.hide = function(){
31713         this.setLeftTop(-100,-100);
31714         this.setStyle("visibility", "hidden");
31715     };
31716     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
31717     // temporarily disabled
31718     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
31719     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
31720 };
31721 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
31722     proxyOffsets : [-4, -9],
31723     fly: Roo.Element.fly,
31724
31725     getTargetFromEvent : function(e){
31726         var t = Roo.lib.Event.getTarget(e);
31727         var cindex = this.view.findCellIndex(t);
31728         if(cindex !== false){
31729             return this.view.getHeaderCell(cindex);
31730         }
31731     },
31732
31733     nextVisible : function(h){
31734         var v = this.view, cm = this.grid.colModel;
31735         h = h.nextSibling;
31736         while(h){
31737             if(!cm.isHidden(v.getCellIndex(h))){
31738                 return h;
31739             }
31740             h = h.nextSibling;
31741         }
31742         return null;
31743     },
31744
31745     prevVisible : function(h){
31746         var v = this.view, cm = this.grid.colModel;
31747         h = h.prevSibling;
31748         while(h){
31749             if(!cm.isHidden(v.getCellIndex(h))){
31750                 return h;
31751             }
31752             h = h.prevSibling;
31753         }
31754         return null;
31755     },
31756
31757     positionIndicator : function(h, n, e){
31758         var x = Roo.lib.Event.getPageX(e);
31759         var r = Roo.lib.Dom.getRegion(n.firstChild);
31760         var px, pt, py = r.top + this.proxyOffsets[1];
31761         if((r.right - x) <= (r.right-r.left)/2){
31762             px = r.right+this.view.borderWidth;
31763             pt = "after";
31764         }else{
31765             px = r.left;
31766             pt = "before";
31767         }
31768         var oldIndex = this.view.getCellIndex(h);
31769         var newIndex = this.view.getCellIndex(n);
31770
31771         if(this.grid.colModel.isFixed(newIndex)){
31772             return false;
31773         }
31774
31775         var locked = this.grid.colModel.isLocked(newIndex);
31776
31777         if(pt == "after"){
31778             newIndex++;
31779         }
31780         if(oldIndex < newIndex){
31781             newIndex--;
31782         }
31783         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
31784             return false;
31785         }
31786         px +=  this.proxyOffsets[0];
31787         this.proxyTop.setLeftTop(px, py);
31788         this.proxyTop.show();
31789         if(!this.bottomOffset){
31790             this.bottomOffset = this.view.mainHd.getHeight();
31791         }
31792         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
31793         this.proxyBottom.show();
31794         return pt;
31795     },
31796
31797     onNodeEnter : function(n, dd, e, data){
31798         if(data.header != n){
31799             this.positionIndicator(data.header, n, e);
31800         }
31801     },
31802
31803     onNodeOver : function(n, dd, e, data){
31804         var result = false;
31805         if(data.header != n){
31806             result = this.positionIndicator(data.header, n, e);
31807         }
31808         if(!result){
31809             this.proxyTop.hide();
31810             this.proxyBottom.hide();
31811         }
31812         return result ? this.dropAllowed : this.dropNotAllowed;
31813     },
31814
31815     onNodeOut : function(n, dd, e, data){
31816         this.proxyTop.hide();
31817         this.proxyBottom.hide();
31818     },
31819
31820     onNodeDrop : function(n, dd, e, data){
31821         var h = data.header;
31822         if(h != n){
31823             var cm = this.grid.colModel;
31824             var x = Roo.lib.Event.getPageX(e);
31825             var r = Roo.lib.Dom.getRegion(n.firstChild);
31826             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
31827             var oldIndex = this.view.getCellIndex(h);
31828             var newIndex = this.view.getCellIndex(n);
31829             var locked = cm.isLocked(newIndex);
31830             if(pt == "after"){
31831                 newIndex++;
31832             }
31833             if(oldIndex < newIndex){
31834                 newIndex--;
31835             }
31836             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
31837                 return false;
31838             }
31839             cm.setLocked(oldIndex, locked, true);
31840             cm.moveColumn(oldIndex, newIndex);
31841             this.grid.fireEvent("columnmove", oldIndex, newIndex);
31842             return true;
31843         }
31844         return false;
31845     }
31846 });
31847 /*
31848  * Based on:
31849  * Ext JS Library 1.1.1
31850  * Copyright(c) 2006-2007, Ext JS, LLC.
31851  *
31852  * Originally Released Under LGPL - original licence link has changed is not relivant.
31853  *
31854  * Fork - LGPL
31855  * <script type="text/javascript">
31856  */
31857   
31858 /**
31859  * @class Roo.grid.GridView
31860  * @extends Roo.util.Observable
31861  *
31862  * @constructor
31863  * @param {Object} config
31864  */
31865 Roo.grid.GridView = function(config){
31866     Roo.grid.GridView.superclass.constructor.call(this);
31867     this.el = null;
31868
31869     Roo.apply(this, config);
31870 };
31871
31872 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
31873
31874     /**
31875      * Override this function to apply custom css classes to rows during rendering
31876      * @param {Record} record The record
31877      * @param {Number} index
31878      * @method getRowClass
31879      */
31880     rowClass : "x-grid-row",
31881
31882     cellClass : "x-grid-col",
31883
31884     tdClass : "x-grid-td",
31885
31886     hdClass : "x-grid-hd",
31887
31888     splitClass : "x-grid-split",
31889
31890     sortClasses : ["sort-asc", "sort-desc"],
31891
31892     enableMoveAnim : false,
31893
31894     hlColor: "C3DAF9",
31895
31896     dh : Roo.DomHelper,
31897
31898     fly : Roo.Element.fly,
31899
31900     css : Roo.util.CSS,
31901
31902     borderWidth: 1,
31903
31904     splitOffset: 3,
31905
31906     scrollIncrement : 22,
31907
31908     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
31909
31910     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
31911
31912     bind : function(ds, cm){
31913         if(this.ds){
31914             this.ds.un("load", this.onLoad, this);
31915             this.ds.un("datachanged", this.onDataChange, this);
31916             this.ds.un("add", this.onAdd, this);
31917             this.ds.un("remove", this.onRemove, this);
31918             this.ds.un("update", this.onUpdate, this);
31919             this.ds.un("clear", this.onClear, this);
31920         }
31921         if(ds){
31922             ds.on("load", this.onLoad, this);
31923             ds.on("datachanged", this.onDataChange, this);
31924             ds.on("add", this.onAdd, this);
31925             ds.on("remove", this.onRemove, this);
31926             ds.on("update", this.onUpdate, this);
31927             ds.on("clear", this.onClear, this);
31928         }
31929         this.ds = ds;
31930
31931         if(this.cm){
31932             this.cm.un("widthchange", this.onColWidthChange, this);
31933             this.cm.un("headerchange", this.onHeaderChange, this);
31934             this.cm.un("hiddenchange", this.onHiddenChange, this);
31935             this.cm.un("columnmoved", this.onColumnMove, this);
31936             this.cm.un("columnlockchange", this.onColumnLock, this);
31937         }
31938         if(cm){
31939             this.generateRules(cm);
31940             cm.on("widthchange", this.onColWidthChange, this);
31941             cm.on("headerchange", this.onHeaderChange, this);
31942             cm.on("hiddenchange", this.onHiddenChange, this);
31943             cm.on("columnmoved", this.onColumnMove, this);
31944             cm.on("columnlockchange", this.onColumnLock, this);
31945         }
31946         this.cm = cm;
31947     },
31948
31949     init: function(grid){
31950                 Roo.grid.GridView.superclass.init.call(this, grid);
31951
31952                 this.bind(grid.dataSource, grid.colModel);
31953
31954             grid.on("headerclick", this.handleHeaderClick, this);
31955
31956         if(grid.trackMouseOver){
31957             grid.on("mouseover", this.onRowOver, this);
31958                 grid.on("mouseout", this.onRowOut, this);
31959             }
31960             grid.cancelTextSelection = function(){};
31961                 this.gridId = grid.id;
31962
31963                 var tpls = this.templates || {};
31964
31965                 if(!tpls.master){
31966                     tpls.master = new Roo.Template(
31967                        '<div class="x-grid" hidefocus="true">',
31968                           '<div class="x-grid-topbar"></div>',
31969                           '<div class="x-grid-scroller"><div></div></div>',
31970                           '<div class="x-grid-locked">',
31971                               '<div class="x-grid-header">{lockedHeader}</div>',
31972                               '<div class="x-grid-body">{lockedBody}</div>',
31973                           "</div>",
31974                           '<div class="x-grid-viewport">',
31975                               '<div class="x-grid-header">{header}</div>',
31976                               '<div class="x-grid-body">{body}</div>',
31977                           "</div>",
31978                           '<div class="x-grid-bottombar"></div>',
31979                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
31980                           '<div class="x-grid-resize-proxy">&#160;</div>',
31981                        "</div>"
31982                     );
31983                     tpls.master.disableformats = true;
31984                 }
31985
31986                 if(!tpls.header){
31987                     tpls.header = new Roo.Template(
31988                        '<table border="0" cellspacing="0" cellpadding="0">',
31989                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
31990                        "</table>{splits}"
31991                     );
31992                     tpls.header.disableformats = true;
31993                 }
31994                 tpls.header.compile();
31995
31996                 if(!tpls.hcell){
31997                     tpls.hcell = new Roo.Template(
31998                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
31999                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32000                         "</div></td>"
32001                      );
32002                      tpls.hcell.disableFormats = true;
32003                 }
32004                 tpls.hcell.compile();
32005
32006                 if(!tpls.hsplit){
32007                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32008                     tpls.hsplit.disableFormats = true;
32009                 }
32010                 tpls.hsplit.compile();
32011
32012                 if(!tpls.body){
32013                     tpls.body = new Roo.Template(
32014                        '<table border="0" cellspacing="0" cellpadding="0">',
32015                        "<tbody>{rows}</tbody>",
32016                        "</table>"
32017                     );
32018                     tpls.body.disableFormats = true;
32019                 }
32020                 tpls.body.compile();
32021
32022                 if(!tpls.row){
32023                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32024                     tpls.row.disableFormats = true;
32025                 }
32026                 tpls.row.compile();
32027
32028                 if(!tpls.cell){
32029                     tpls.cell = new Roo.Template(
32030                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32031                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32032                         "</td>"
32033                     );
32034             tpls.cell.disableFormats = true;
32035         }
32036                 tpls.cell.compile();
32037
32038                 this.templates = tpls;
32039         },
32040
32041         // remap these for backwards compat
32042     onColWidthChange : function(){
32043         this.updateColumns.apply(this, arguments);
32044     },
32045     onHeaderChange : function(){
32046         this.updateHeaders.apply(this, arguments);
32047     }, 
32048     onHiddenChange : function(){
32049         this.handleHiddenChange.apply(this, arguments);
32050     },
32051     onColumnMove : function(){
32052         this.handleColumnMove.apply(this, arguments);
32053     },
32054     onColumnLock : function(){
32055         this.handleLockChange.apply(this, arguments);
32056     },
32057
32058     onDataChange : function(){
32059         this.refresh();
32060         this.updateHeaderSortState();
32061     },
32062
32063         onClear : function(){
32064         this.refresh();
32065     },
32066
32067         onUpdate : function(ds, record){
32068         this.refreshRow(record);
32069     },
32070
32071     refreshRow : function(record){
32072         var ds = this.ds, index;
32073         if(typeof record == 'number'){
32074             index = record;
32075             record = ds.getAt(index);
32076         }else{
32077             index = ds.indexOf(record);
32078         }
32079         this.insertRows(ds, index, index, true);
32080         this.onRemove(ds, record, index+1, true);
32081         this.syncRowHeights(index, index);
32082         this.layout();
32083         this.fireEvent("rowupdated", this, index, record);
32084     },
32085
32086     onAdd : function(ds, records, index){
32087         this.insertRows(ds, index, index + (records.length-1));
32088     },
32089
32090     onRemove : function(ds, record, index, isUpdate){
32091         if(isUpdate !== true){
32092             this.fireEvent("beforerowremoved", this, index, record);
32093         }
32094         var bt = this.getBodyTable(), lt = this.getLockedTable();
32095         if(bt.rows[index]){
32096             bt.firstChild.removeChild(bt.rows[index]);
32097         }
32098         if(lt.rows[index]){
32099             lt.firstChild.removeChild(lt.rows[index]);
32100         }
32101         if(isUpdate !== true){
32102             this.stripeRows(index);
32103             this.syncRowHeights(index, index);
32104             this.layout();
32105             this.fireEvent("rowremoved", this, index, record);
32106         }
32107     },
32108
32109     onLoad : function(){
32110         this.scrollToTop();
32111     },
32112
32113     /**
32114      * Scrolls the grid to the top
32115      */
32116     scrollToTop : function(){
32117         if(this.scroller){
32118             this.scroller.dom.scrollTop = 0;
32119             this.syncScroll();
32120         }
32121     },
32122
32123     /**
32124      * Gets a panel in the header of the grid that can be used for toolbars etc.
32125      * After modifying the contents of this panel a call to grid.autoSize() may be
32126      * required to register any changes in size.
32127      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32128      * @return Roo.Element
32129      */
32130     getHeaderPanel : function(doShow){
32131         if(doShow){
32132             this.headerPanel.show();
32133         }
32134         return this.headerPanel;
32135         },
32136
32137         /**
32138      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32139      * After modifying the contents of this panel a call to grid.autoSize() may be
32140      * required to register any changes in size.
32141      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32142      * @return Roo.Element
32143      */
32144     getFooterPanel : function(doShow){
32145         if(doShow){
32146             this.footerPanel.show();
32147         }
32148         return this.footerPanel;
32149         },
32150
32151         initElements : function(){
32152             var E = Roo.Element;
32153             var el = this.grid.getGridEl().dom.firstChild;
32154             var cs = el.childNodes;
32155
32156             this.el = new E(el);
32157             this.headerPanel = new E(el.firstChild);
32158             this.headerPanel.enableDisplayMode("block");
32159
32160         this.scroller = new E(cs[1]);
32161             this.scrollSizer = new E(this.scroller.dom.firstChild);
32162
32163             this.lockedWrap = new E(cs[2]);
32164             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
32165             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
32166
32167             this.mainWrap = new E(cs[3]);
32168             this.mainHd = new E(this.mainWrap.dom.firstChild);
32169             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
32170
32171             this.footerPanel = new E(cs[4]);
32172             this.footerPanel.enableDisplayMode("block");
32173
32174         this.focusEl = new E(cs[5]);
32175         this.focusEl.swallowEvent("click", true);
32176         this.resizeProxy = new E(cs[6]);
32177
32178             this.headerSelector = String.format(
32179                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
32180                this.lockedHd.id, this.mainHd.id
32181             );
32182
32183             this.splitterSelector = String.format(
32184                '#{0} div.x-grid-split, #{1} div.x-grid-split',
32185                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
32186             );
32187     },
32188     idToCssName : function(s)
32189     {
32190         return s.replace(/[^a-z0-9]+/ig, '-');
32191     },
32192
32193         getHeaderCell : function(index){
32194             return Roo.DomQuery.select(this.headerSelector)[index];
32195         },
32196
32197         getHeaderCellMeasure : function(index){
32198             return this.getHeaderCell(index).firstChild;
32199         },
32200
32201         getHeaderCellText : function(index){
32202             return this.getHeaderCell(index).firstChild.firstChild;
32203         },
32204
32205         getLockedTable : function(){
32206             return this.lockedBody.dom.firstChild;
32207         },
32208
32209         getBodyTable : function(){
32210             return this.mainBody.dom.firstChild;
32211         },
32212
32213         getLockedRow : function(index){
32214             return this.getLockedTable().rows[index];
32215         },
32216
32217         getRow : function(index){
32218             return this.getBodyTable().rows[index];
32219         },
32220
32221         getRowComposite : function(index){
32222             if(!this.rowEl){
32223                 this.rowEl = new Roo.CompositeElementLite();
32224             }
32225         var els = [], lrow, mrow;
32226         if(lrow = this.getLockedRow(index)){
32227             els.push(lrow);
32228         }
32229         if(mrow = this.getRow(index)){
32230             els.push(mrow);
32231         }
32232         this.rowEl.elements = els;
32233             return this.rowEl;
32234         },
32235
32236         getCell : function(rowIndex, colIndex){
32237             var locked = this.cm.getLockedCount();
32238             var source;
32239             if(colIndex < locked){
32240                 source = this.lockedBody.dom.firstChild;
32241             }else{
32242                 source = this.mainBody.dom.firstChild;
32243                 colIndex -= locked;
32244             }
32245         return source.rows[rowIndex].childNodes[colIndex];
32246         },
32247
32248         getCellText : function(rowIndex, colIndex){
32249             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
32250         },
32251
32252         getCellBox : function(cell){
32253             var b = this.fly(cell).getBox();
32254         if(Roo.isOpera){ // opera fails to report the Y
32255             b.y = cell.offsetTop + this.mainBody.getY();
32256         }
32257         return b;
32258     },
32259
32260     getCellIndex : function(cell){
32261         var id = String(cell.className).match(this.cellRE);
32262         if(id){
32263             return parseInt(id[1], 10);
32264         }
32265         return 0;
32266     },
32267
32268     findHeaderIndex : function(n){
32269         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32270         return r ? this.getCellIndex(r) : false;
32271     },
32272
32273     findHeaderCell : function(n){
32274         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32275         return r ? r : false;
32276     },
32277
32278     findRowIndex : function(n){
32279         if(!n){
32280             return false;
32281         }
32282         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
32283         return r ? r.rowIndex : false;
32284     },
32285
32286     findCellIndex : function(node){
32287         var stop = this.el.dom;
32288         while(node && node != stop){
32289             if(this.findRE.test(node.className)){
32290                 return this.getCellIndex(node);
32291             }
32292             node = node.parentNode;
32293         }
32294         return false;
32295     },
32296
32297     getColumnId : function(index){
32298             return this.cm.getColumnId(index);
32299         },
32300
32301         getSplitters : function(){
32302             if(this.splitterSelector){
32303                return Roo.DomQuery.select(this.splitterSelector);
32304             }else{
32305                 return null;
32306             }
32307         },
32308
32309         getSplitter : function(index){
32310             return this.getSplitters()[index];
32311         },
32312
32313     onRowOver : function(e, t){
32314         var row;
32315         if((row = this.findRowIndex(t)) !== false){
32316             this.getRowComposite(row).addClass("x-grid-row-over");
32317         }
32318     },
32319
32320     onRowOut : function(e, t){
32321         var row;
32322         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
32323             this.getRowComposite(row).removeClass("x-grid-row-over");
32324         }
32325     },
32326
32327     renderHeaders : function(){
32328             var cm = this.cm;
32329         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
32330         var cb = [], lb = [], sb = [], lsb = [], p = {};
32331         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32332             p.cellId = "x-grid-hd-0-" + i;
32333             p.splitId = "x-grid-csplit-0-" + i;
32334             p.id = cm.getColumnId(i);
32335             p.title = cm.getColumnTooltip(i) || "";
32336             p.value = cm.getColumnHeader(i) || "";
32337             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
32338             if(!cm.isLocked(i)){
32339                 cb[cb.length] = ct.apply(p);
32340                 sb[sb.length] = st.apply(p);
32341             }else{
32342                 lb[lb.length] = ct.apply(p);
32343                 lsb[lsb.length] = st.apply(p);
32344             }
32345         }
32346         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
32347                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
32348         },
32349
32350         updateHeaders : function(){
32351         var html = this.renderHeaders();
32352         this.lockedHd.update(html[0]);
32353         this.mainHd.update(html[1]);
32354     },
32355
32356     /**
32357      * Focuses the specified row.
32358      * @param {Number} row The row index
32359      */
32360     focusRow : function(row){
32361         var x = this.scroller.dom.scrollLeft;
32362         this.focusCell(row, 0, false);
32363         this.scroller.dom.scrollLeft = x;
32364     },
32365
32366     /**
32367      * Focuses the specified cell.
32368      * @param {Number} row The row index
32369      * @param {Number} col The column index
32370      * @param {Boolean} hscroll false to disable horizontal scrolling
32371      */
32372     focusCell : function(row, col, hscroll){
32373         var el = this.ensureVisible(row, col, hscroll);
32374         this.focusEl.alignTo(el, "tl-tl");
32375         if(Roo.isGecko){
32376             this.focusEl.focus();
32377         }else{
32378             this.focusEl.focus.defer(1, this.focusEl);
32379         }
32380     },
32381
32382     /**
32383      * Scrolls the specified cell into view
32384      * @param {Number} row The row index
32385      * @param {Number} col The column index
32386      * @param {Boolean} hscroll false to disable horizontal scrolling
32387      */
32388     ensureVisible : function(row, col, hscroll){
32389         if(typeof row != "number"){
32390             row = row.rowIndex;
32391         }
32392         if(row < 0 && row >= this.ds.getCount()){
32393             return;
32394         }
32395         col = (col !== undefined ? col : 0);
32396         var cm = this.grid.colModel;
32397         while(cm.isHidden(col)){
32398             col++;
32399         }
32400
32401         var el = this.getCell(row, col);
32402         if(!el){
32403             return;
32404         }
32405         var c = this.scroller.dom;
32406
32407         var ctop = parseInt(el.offsetTop, 10);
32408         var cleft = parseInt(el.offsetLeft, 10);
32409         var cbot = ctop + el.offsetHeight;
32410         var cright = cleft + el.offsetWidth;
32411
32412         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
32413         var stop = parseInt(c.scrollTop, 10);
32414         var sleft = parseInt(c.scrollLeft, 10);
32415         var sbot = stop + ch;
32416         var sright = sleft + c.clientWidth;
32417
32418         if(ctop < stop){
32419                 c.scrollTop = ctop;
32420         }else if(cbot > sbot){
32421             c.scrollTop = cbot-ch;
32422         }
32423
32424         if(hscroll !== false){
32425             if(cleft < sleft){
32426                 c.scrollLeft = cleft;
32427             }else if(cright > sright){
32428                 c.scrollLeft = cright-c.clientWidth;
32429             }
32430         }
32431         return el;
32432     },
32433
32434     updateColumns : function(){
32435         this.grid.stopEditing();
32436         var cm = this.grid.colModel, colIds = this.getColumnIds();
32437         //var totalWidth = cm.getTotalWidth();
32438         var pos = 0;
32439         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32440             //if(cm.isHidden(i)) continue;
32441             var w = cm.getColumnWidth(i);
32442             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32443             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32444         }
32445         this.updateSplitters();
32446     },
32447
32448     generateRules : function(cm){
32449         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
32450         Roo.util.CSS.removeStyleSheet(rulesId);
32451         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32452             var cid = cm.getColumnId(i);
32453             var align = '';
32454             if(cm.config[i].align){
32455                 align = 'text-align:'+cm.config[i].align+';';
32456             }
32457             var hidden = '';
32458             if(cm.isHidden(i)){
32459                 hidden = 'display:none;';
32460             }
32461             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
32462             ruleBuf.push(
32463                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
32464                     this.hdSelector, cid, " {\n", align, width, "}\n",
32465                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
32466                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
32467         }
32468         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32469     },
32470
32471     updateSplitters : function(){
32472         var cm = this.cm, s = this.getSplitters();
32473         if(s){ // splitters not created yet
32474             var pos = 0, locked = true;
32475             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32476                 if(cm.isHidden(i)) continue;
32477                 var w = cm.getColumnWidth(i);
32478                 if(!cm.isLocked(i) && locked){
32479                     pos = 0;
32480                     locked = false;
32481                 }
32482                 pos += w;
32483                 s[i].style.left = (pos-this.splitOffset) + "px";
32484             }
32485         }
32486     },
32487
32488     handleHiddenChange : function(colModel, colIndex, hidden){
32489         if(hidden){
32490             this.hideColumn(colIndex);
32491         }else{
32492             this.unhideColumn(colIndex);
32493         }
32494     },
32495
32496     hideColumn : function(colIndex){
32497         var cid = this.getColumnId(colIndex);
32498         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
32499         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
32500         if(Roo.isSafari){
32501             this.updateHeaders();
32502         }
32503         this.updateSplitters();
32504         this.layout();
32505     },
32506
32507     unhideColumn : function(colIndex){
32508         var cid = this.getColumnId(colIndex);
32509         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
32510         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
32511
32512         if(Roo.isSafari){
32513             this.updateHeaders();
32514         }
32515         this.updateSplitters();
32516         this.layout();
32517     },
32518
32519     insertRows : function(dm, firstRow, lastRow, isUpdate){
32520         if(firstRow == 0 && lastRow == dm.getCount()-1){
32521             this.refresh();
32522         }else{
32523             if(!isUpdate){
32524                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
32525             }
32526             var s = this.getScrollState();
32527             var markup = this.renderRows(firstRow, lastRow);
32528             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
32529             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
32530             this.restoreScroll(s);
32531             if(!isUpdate){
32532                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
32533                 this.syncRowHeights(firstRow, lastRow);
32534                 this.stripeRows(firstRow);
32535                 this.layout();
32536             }
32537         }
32538     },
32539
32540     bufferRows : function(markup, target, index){
32541         var before = null, trows = target.rows, tbody = target.tBodies[0];
32542         if(index < trows.length){
32543             before = trows[index];
32544         }
32545         var b = document.createElement("div");
32546         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
32547         var rows = b.firstChild.rows;
32548         for(var i = 0, len = rows.length; i < len; i++){
32549             if(before){
32550                 tbody.insertBefore(rows[0], before);
32551             }else{
32552                 tbody.appendChild(rows[0]);
32553             }
32554         }
32555         b.innerHTML = "";
32556         b = null;
32557     },
32558
32559     deleteRows : function(dm, firstRow, lastRow){
32560         if(dm.getRowCount()<1){
32561             this.fireEvent("beforerefresh", this);
32562             this.mainBody.update("");
32563             this.lockedBody.update("");
32564             this.fireEvent("refresh", this);
32565         }else{
32566             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
32567             var bt = this.getBodyTable();
32568             var tbody = bt.firstChild;
32569             var rows = bt.rows;
32570             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
32571                 tbody.removeChild(rows[firstRow]);
32572             }
32573             this.stripeRows(firstRow);
32574             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
32575         }
32576     },
32577
32578     updateRows : function(dataSource, firstRow, lastRow){
32579         var s = this.getScrollState();
32580         this.refresh();
32581         this.restoreScroll(s);
32582     },
32583
32584     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
32585         if(!noRefresh){
32586            this.refresh();
32587         }
32588         this.updateHeaderSortState();
32589     },
32590
32591     getScrollState : function(){
32592         var sb = this.scroller.dom;
32593         return {left: sb.scrollLeft, top: sb.scrollTop};
32594     },
32595
32596     stripeRows : function(startRow){
32597         if(!this.grid.stripeRows || this.ds.getCount() < 1){
32598             return;
32599         }
32600         startRow = startRow || 0;
32601         var rows = this.getBodyTable().rows;
32602         var lrows = this.getLockedTable().rows;
32603         var cls = ' x-grid-row-alt ';
32604         for(var i = startRow, len = rows.length; i < len; i++){
32605             var row = rows[i], lrow = lrows[i];
32606             var isAlt = ((i+1) % 2 == 0);
32607             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
32608             if(isAlt == hasAlt){
32609                 continue;
32610             }
32611             if(isAlt){
32612                 row.className += " x-grid-row-alt";
32613             }else{
32614                 row.className = row.className.replace("x-grid-row-alt", "");
32615             }
32616             if(lrow){
32617                 lrow.className = row.className;
32618             }
32619         }
32620     },
32621
32622     restoreScroll : function(state){
32623         var sb = this.scroller.dom;
32624         sb.scrollLeft = state.left;
32625         sb.scrollTop = state.top;
32626         this.syncScroll();
32627     },
32628
32629     syncScroll : function(){
32630         var sb = this.scroller.dom;
32631         var sh = this.mainHd.dom;
32632         var bs = this.mainBody.dom;
32633         var lv = this.lockedBody.dom;
32634         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
32635         lv.scrollTop = bs.scrollTop = sb.scrollTop;
32636     },
32637
32638     handleScroll : function(e){
32639         this.syncScroll();
32640         var sb = this.scroller.dom;
32641         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
32642         e.stopEvent();
32643     },
32644
32645     handleWheel : function(e){
32646         var d = e.getWheelDelta();
32647         this.scroller.dom.scrollTop -= d*22;
32648         // set this here to prevent jumpy scrolling on large tables
32649         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
32650         e.stopEvent();
32651     },
32652
32653     renderRows : function(startRow, endRow){
32654         // pull in all the crap needed to render rows
32655         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
32656         var colCount = cm.getColumnCount();
32657
32658         if(ds.getCount() < 1){
32659             return ["", ""];
32660         }
32661
32662         // build a map for all the columns
32663         var cs = [];
32664         for(var i = 0; i < colCount; i++){
32665             var name = cm.getDataIndex(i);
32666             cs[i] = {
32667                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
32668                 renderer : cm.getRenderer(i),
32669                 id : cm.getColumnId(i),
32670                 locked : cm.isLocked(i)
32671             };
32672         }
32673
32674         startRow = startRow || 0;
32675         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
32676
32677         // records to render
32678         var rs = ds.getRange(startRow, endRow);
32679
32680         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
32681     },
32682
32683     // As much as I hate to duplicate code, this was branched because FireFox really hates
32684     // [].join("") on strings. The performance difference was substantial enough to
32685     // branch this function
32686     doRender : Roo.isGecko ?
32687             function(cs, rs, ds, startRow, colCount, stripe){
32688                 var ts = this.templates, ct = ts.cell, rt = ts.row;
32689                 // buffers
32690                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
32691                 for(var j = 0, len = rs.length; j < len; j++){
32692                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
32693                     for(var i = 0; i < colCount; i++){
32694                         c = cs[i];
32695                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
32696                         p.id = c.id;
32697                         p.css = p.attr = "";
32698                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
32699                         if(p.value == undefined || p.value === "") p.value = "&#160;";
32700                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
32701                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
32702                         }
32703                         var markup = ct.apply(p);
32704                         if(!c.locked){
32705                             cb+= markup;
32706                         }else{
32707                             lcb+= markup;
32708                         }
32709                     }
32710                     var alt = [];
32711                     if(stripe && ((rowIndex+1) % 2 == 0)){
32712                         alt[0] = "x-grid-row-alt";
32713                     }
32714                     if(r.dirty){
32715                         alt[1] = " x-grid-dirty-row";
32716                     }
32717                     rp.cells = lcb;
32718                     if(this.getRowClass){
32719                         alt[2] = this.getRowClass(r, rowIndex);
32720                     }
32721                     rp.alt = alt.join(" ");
32722                     lbuf+= rt.apply(rp);
32723                     rp.cells = cb;
32724                     buf+=  rt.apply(rp);
32725                 }
32726                 return [lbuf, buf];
32727             } :
32728             function(cs, rs, ds, startRow, colCount, stripe){
32729                 var ts = this.templates, ct = ts.cell, rt = ts.row;
32730                 // buffers
32731                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
32732                 for(var j = 0, len = rs.length; j < len; j++){
32733                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
32734                     for(var i = 0; i < colCount; i++){
32735                         c = cs[i];
32736                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
32737                         p.id = c.id;
32738                         p.css = p.attr = "";
32739                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
32740                         if(p.value == undefined || p.value === "") p.value = "&#160;";
32741                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
32742                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
32743                         }
32744                         var markup = ct.apply(p);
32745                         if(!c.locked){
32746                             cb[cb.length] = markup;
32747                         }else{
32748                             lcb[lcb.length] = markup;
32749                         }
32750                     }
32751                     var alt = [];
32752                     if(stripe && ((rowIndex+1) % 2 == 0)){
32753                         alt[0] = "x-grid-row-alt";
32754                     }
32755                     if(r.dirty){
32756                         alt[1] = " x-grid-dirty-row";
32757                     }
32758                     rp.cells = lcb;
32759                     if(this.getRowClass){
32760                         alt[2] = this.getRowClass(r, rowIndex);
32761                     }
32762                     rp.alt = alt.join(" ");
32763                     rp.cells = lcb.join("");
32764                     lbuf[lbuf.length] = rt.apply(rp);
32765                     rp.cells = cb.join("");
32766                     buf[buf.length] =  rt.apply(rp);
32767                 }
32768                 return [lbuf.join(""), buf.join("")];
32769             },
32770
32771     renderBody : function(){
32772         var markup = this.renderRows();
32773         var bt = this.templates.body;
32774         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
32775     },
32776
32777     /**
32778      * Refreshes the grid
32779      * @param {Boolean} headersToo
32780      */
32781     refresh : function(headersToo){
32782         this.fireEvent("beforerefresh", this);
32783         this.grid.stopEditing();
32784         var result = this.renderBody();
32785         this.lockedBody.update(result[0]);
32786         this.mainBody.update(result[1]);
32787         if(headersToo === true){
32788             this.updateHeaders();
32789             this.updateColumns();
32790             this.updateSplitters();
32791             this.updateHeaderSortState();
32792         }
32793         this.syncRowHeights();
32794         this.layout();
32795         this.fireEvent("refresh", this);
32796     },
32797
32798     handleColumnMove : function(cm, oldIndex, newIndex){
32799         this.indexMap = null;
32800         var s = this.getScrollState();
32801         this.refresh(true);
32802         this.restoreScroll(s);
32803         this.afterMove(newIndex);
32804     },
32805
32806     afterMove : function(colIndex){
32807         if(this.enableMoveAnim && Roo.enableFx){
32808             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
32809         }
32810     },
32811
32812     updateCell : function(dm, rowIndex, dataIndex){
32813         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
32814         if(typeof colIndex == "undefined"){ // not present in grid
32815             return;
32816         }
32817         var cm = this.grid.colModel;
32818         var cell = this.getCell(rowIndex, colIndex);
32819         var cellText = this.getCellText(rowIndex, colIndex);
32820
32821         var p = {
32822             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
32823             id : cm.getColumnId(colIndex),
32824             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
32825         };
32826         var renderer = cm.getRenderer(colIndex);
32827         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
32828         if(typeof val == "undefined" || val === "") val = "&#160;";
32829         cellText.innerHTML = val;
32830         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
32831         this.syncRowHeights(rowIndex, rowIndex);
32832     },
32833
32834     calcColumnWidth : function(colIndex, maxRowsToMeasure){
32835         var maxWidth = 0;
32836         if(this.grid.autoSizeHeaders){
32837             var h = this.getHeaderCellMeasure(colIndex);
32838             maxWidth = Math.max(maxWidth, h.scrollWidth);
32839         }
32840         var tb, index;
32841         if(this.cm.isLocked(colIndex)){
32842             tb = this.getLockedTable();
32843             index = colIndex;
32844         }else{
32845             tb = this.getBodyTable();
32846             index = colIndex - this.cm.getLockedCount();
32847         }
32848         if(tb && tb.rows){
32849             var rows = tb.rows;
32850             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
32851             for(var i = 0; i < stopIndex; i++){
32852                 var cell = rows[i].childNodes[index].firstChild;
32853                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
32854             }
32855         }
32856         return maxWidth + /*margin for error in IE*/ 5;
32857     },
32858     /**
32859      * Autofit a column to its content.
32860      * @param {Number} colIndex
32861      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
32862      */
32863      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
32864          if(this.cm.isHidden(colIndex)){
32865              return; // can't calc a hidden column
32866          }
32867         if(forceMinSize){
32868             var cid = this.cm.getColumnId(colIndex);
32869             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
32870            if(this.grid.autoSizeHeaders){
32871                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
32872            }
32873         }
32874         var newWidth = this.calcColumnWidth(colIndex);
32875         this.cm.setColumnWidth(colIndex,
32876             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
32877         if(!suppressEvent){
32878             this.grid.fireEvent("columnresize", colIndex, newWidth);
32879         }
32880     },
32881
32882     /**
32883      * Autofits all columns to their content and then expands to fit any extra space in the grid
32884      */
32885      autoSizeColumns : function(){
32886         var cm = this.grid.colModel;
32887         var colCount = cm.getColumnCount();
32888         for(var i = 0; i < colCount; i++){
32889             this.autoSizeColumn(i, true, true);
32890         }
32891         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
32892             this.fitColumns();
32893         }else{
32894             this.updateColumns();
32895             this.layout();
32896         }
32897     },
32898
32899     /**
32900      * Autofits all columns to the grid's width proportionate with their current size
32901      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
32902      */
32903     fitColumns : function(reserveScrollSpace){
32904         var cm = this.grid.colModel;
32905         var colCount = cm.getColumnCount();
32906         var cols = [];
32907         var width = 0;
32908         var i, w;
32909         for (i = 0; i < colCount; i++){
32910             if(!cm.isHidden(i) && !cm.isFixed(i)){
32911                 w = cm.getColumnWidth(i);
32912                 cols.push(i);
32913                 cols.push(w);
32914                 width += w;
32915             }
32916         }
32917         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
32918         if(reserveScrollSpace){
32919             avail -= 17;
32920         }
32921         var frac = (avail - cm.getTotalWidth())/width;
32922         while (cols.length){
32923             w = cols.pop();
32924             i = cols.pop();
32925             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
32926         }
32927         this.updateColumns();
32928         this.layout();
32929     },
32930
32931     onRowSelect : function(rowIndex){
32932         var row = this.getRowComposite(rowIndex);
32933         row.addClass("x-grid-row-selected");
32934     },
32935
32936     onRowDeselect : function(rowIndex){
32937         var row = this.getRowComposite(rowIndex);
32938         row.removeClass("x-grid-row-selected");
32939     },
32940
32941     onCellSelect : function(row, col){
32942         var cell = this.getCell(row, col);
32943         if(cell){
32944             Roo.fly(cell).addClass("x-grid-cell-selected");
32945         }
32946     },
32947
32948     onCellDeselect : function(row, col){
32949         var cell = this.getCell(row, col);
32950         if(cell){
32951             Roo.fly(cell).removeClass("x-grid-cell-selected");
32952         }
32953     },
32954
32955     updateHeaderSortState : function(){
32956         var state = this.ds.getSortState();
32957         if(!state){
32958             return;
32959         }
32960         this.sortState = state;
32961         var sortColumn = this.cm.findColumnIndex(state.field);
32962         if(sortColumn != -1){
32963             var sortDir = state.direction;
32964             var sc = this.sortClasses;
32965             var hds = this.el.select(this.headerSelector).removeClass(sc);
32966             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
32967         }
32968     },
32969
32970     handleHeaderClick : function(g, index){
32971         if(this.headersDisabled){
32972             return;
32973         }
32974         var dm = g.dataSource, cm = g.colModel;
32975             if(!cm.isSortable(index)){
32976             return;
32977         }
32978             g.stopEditing();
32979         dm.sort(cm.getDataIndex(index));
32980     },
32981
32982
32983     destroy : function(){
32984         if(this.colMenu){
32985             this.colMenu.removeAll();
32986             Roo.menu.MenuMgr.unregister(this.colMenu);
32987             this.colMenu.getEl().remove();
32988             delete this.colMenu;
32989         }
32990         if(this.hmenu){
32991             this.hmenu.removeAll();
32992             Roo.menu.MenuMgr.unregister(this.hmenu);
32993             this.hmenu.getEl().remove();
32994             delete this.hmenu;
32995         }
32996         if(this.grid.enableColumnMove){
32997             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
32998             if(dds){
32999                 for(var dd in dds){
33000                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33001                         var elid = dds[dd].dragElId;
33002                         dds[dd].unreg();
33003                         Roo.get(elid).remove();
33004                     } else if(dds[dd].config.isTarget){
33005                         dds[dd].proxyTop.remove();
33006                         dds[dd].proxyBottom.remove();
33007                         dds[dd].unreg();
33008                     }
33009                     if(Roo.dd.DDM.locationCache[dd]){
33010                         delete Roo.dd.DDM.locationCache[dd];
33011                     }
33012                 }
33013                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33014             }
33015         }
33016         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33017         this.bind(null, null);
33018         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33019     },
33020
33021     handleLockChange : function(){
33022         this.refresh(true);
33023     },
33024
33025     onDenyColumnLock : function(){
33026
33027     },
33028
33029     onDenyColumnHide : function(){
33030
33031     },
33032
33033     handleHdMenuClick : function(item){
33034         var index = this.hdCtxIndex;
33035         var cm = this.cm, ds = this.ds;
33036         switch(item.id){
33037             case "asc":
33038                 ds.sort(cm.getDataIndex(index), "ASC");
33039                 break;
33040             case "desc":
33041                 ds.sort(cm.getDataIndex(index), "DESC");
33042                 break;
33043             case "lock":
33044                 var lc = cm.getLockedCount();
33045                 if(cm.getColumnCount(true) <= lc+1){
33046                     this.onDenyColumnLock();
33047                     return;
33048                 }
33049                 if(lc != index){
33050                     cm.setLocked(index, true, true);
33051                     cm.moveColumn(index, lc);
33052                     this.grid.fireEvent("columnmove", index, lc);
33053                 }else{
33054                     cm.setLocked(index, true);
33055                 }
33056             break;
33057             case "unlock":
33058                 var lc = cm.getLockedCount();
33059                 if((lc-1) != index){
33060                     cm.setLocked(index, false, true);
33061                     cm.moveColumn(index, lc-1);
33062                     this.grid.fireEvent("columnmove", index, lc-1);
33063                 }else{
33064                     cm.setLocked(index, false);
33065                 }
33066             break;
33067             default:
33068                 index = cm.getIndexById(item.id.substr(4));
33069                 if(index != -1){
33070                     if(item.checked && cm.getColumnCount(true) <= 1){
33071                         this.onDenyColumnHide();
33072                         return false;
33073                     }
33074                     cm.setHidden(index, item.checked);
33075                 }
33076         }
33077         return true;
33078     },
33079
33080     beforeColMenuShow : function(){
33081         var cm = this.cm,  colCount = cm.getColumnCount();
33082         this.colMenu.removeAll();
33083         for(var i = 0; i < colCount; i++){
33084             this.colMenu.add(new Roo.menu.CheckItem({
33085                 id: "col-"+cm.getColumnId(i),
33086                 text: cm.getColumnHeader(i),
33087                 checked: !cm.isHidden(i),
33088                 hideOnClick:false
33089             }));
33090         }
33091     },
33092
33093     handleHdCtx : function(g, index, e){
33094         e.stopEvent();
33095         var hd = this.getHeaderCell(index);
33096         this.hdCtxIndex = index;
33097         var ms = this.hmenu.items, cm = this.cm;
33098         ms.get("asc").setDisabled(!cm.isSortable(index));
33099         ms.get("desc").setDisabled(!cm.isSortable(index));
33100         if(this.grid.enableColLock !== false){
33101             ms.get("lock").setDisabled(cm.isLocked(index));
33102             ms.get("unlock").setDisabled(!cm.isLocked(index));
33103         }
33104         this.hmenu.show(hd, "tl-bl");
33105     },
33106
33107     handleHdOver : function(e){
33108         var hd = this.findHeaderCell(e.getTarget());
33109         if(hd && !this.headersDisabled){
33110             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33111                this.fly(hd).addClass("x-grid-hd-over");
33112             }
33113         }
33114     },
33115
33116     handleHdOut : function(e){
33117         var hd = this.findHeaderCell(e.getTarget());
33118         if(hd){
33119             this.fly(hd).removeClass("x-grid-hd-over");
33120         }
33121     },
33122
33123     handleSplitDblClick : function(e, t){
33124         var i = this.getCellIndex(t);
33125         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33126             this.autoSizeColumn(i, true);
33127             this.layout();
33128         }
33129     },
33130
33131     render : function(){
33132
33133         var cm = this.cm;
33134         var colCount = cm.getColumnCount();
33135
33136         if(this.grid.monitorWindowResize === true){
33137             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33138         }
33139         var header = this.renderHeaders();
33140         var body = this.templates.body.apply({rows:""});
33141         var html = this.templates.master.apply({
33142             lockedBody: body,
33143             body: body,
33144             lockedHeader: header[0],
33145             header: header[1]
33146         });
33147
33148         //this.updateColumns();
33149
33150         this.grid.getGridEl().dom.innerHTML = html;
33151
33152         this.initElements();
33153
33154         this.scroller.on("scroll", this.handleScroll, this);
33155         this.lockedBody.on("mousewheel", this.handleWheel, this);
33156         this.mainBody.on("mousewheel", this.handleWheel, this);
33157
33158         this.mainHd.on("mouseover", this.handleHdOver, this);
33159         this.mainHd.on("mouseout", this.handleHdOut, this);
33160         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
33161                 {delegate: "."+this.splitClass});
33162
33163         this.lockedHd.on("mouseover", this.handleHdOver, this);
33164         this.lockedHd.on("mouseout", this.handleHdOut, this);
33165         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
33166                 {delegate: "."+this.splitClass});
33167
33168         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
33169             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33170         }
33171
33172         this.updateSplitters();
33173
33174         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
33175             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33176             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33177         }
33178
33179         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
33180             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
33181             this.hmenu.add(
33182                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
33183                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
33184             );
33185             if(this.grid.enableColLock !== false){
33186                 this.hmenu.add('-',
33187                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
33188                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
33189                 );
33190             }
33191             if(this.grid.enableColumnHide !== false){
33192
33193                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
33194                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
33195                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
33196
33197                 this.hmenu.add('-',
33198                     {id:"columns", text: this.columnsText, menu: this.colMenu}
33199                 );
33200             }
33201             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
33202
33203             this.grid.on("headercontextmenu", this.handleHdCtx, this);
33204         }
33205
33206         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
33207             this.dd = new Roo.grid.GridDragZone(this.grid, {
33208                 ddGroup : this.grid.ddGroup || 'GridDD'
33209             });
33210         }
33211
33212         /*
33213         for(var i = 0; i < colCount; i++){
33214             if(cm.isHidden(i)){
33215                 this.hideColumn(i);
33216             }
33217             if(cm.config[i].align){
33218                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
33219                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
33220             }
33221         }*/
33222         
33223         this.updateHeaderSortState();
33224
33225         this.beforeInitialResize();
33226         this.layout(true);
33227
33228         // two part rendering gives faster view to the user
33229         this.renderPhase2.defer(1, this);
33230     },
33231
33232     renderPhase2 : function(){
33233         // render the rows now
33234         this.refresh();
33235         if(this.grid.autoSizeColumns){
33236             this.autoSizeColumns();
33237         }
33238     },
33239
33240     beforeInitialResize : function(){
33241
33242     },
33243
33244     onColumnSplitterMoved : function(i, w){
33245         this.userResized = true;
33246         var cm = this.grid.colModel;
33247         cm.setColumnWidth(i, w, true);
33248         var cid = cm.getColumnId(i);
33249         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33250         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33251         this.updateSplitters();
33252         this.layout();
33253         this.grid.fireEvent("columnresize", i, w);
33254     },
33255
33256     syncRowHeights : function(startIndex, endIndex){
33257         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
33258             startIndex = startIndex || 0;
33259             var mrows = this.getBodyTable().rows;
33260             var lrows = this.getLockedTable().rows;
33261             var len = mrows.length-1;
33262             endIndex = Math.min(endIndex || len, len);
33263             for(var i = startIndex; i <= endIndex; i++){
33264                 var m = mrows[i], l = lrows[i];
33265                 var h = Math.max(m.offsetHeight, l.offsetHeight);
33266                 m.style.height = l.style.height = h + "px";
33267             }
33268         }
33269     },
33270
33271     layout : function(initialRender, is2ndPass){
33272         var g = this.grid;
33273         var auto = g.autoHeight;
33274         var scrollOffset = 16;
33275         var c = g.getGridEl(), cm = this.cm,
33276                 expandCol = g.autoExpandColumn,
33277                 gv = this;
33278         //c.beginMeasure();
33279
33280         if(!c.dom.offsetWidth){ // display:none?
33281             if(initialRender){
33282                 this.lockedWrap.show();
33283                 this.mainWrap.show();
33284             }
33285             return;
33286         }
33287
33288         var hasLock = this.cm.isLocked(0);
33289
33290         var tbh = this.headerPanel.getHeight();
33291         var bbh = this.footerPanel.getHeight();
33292
33293         if(auto){
33294             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
33295             var newHeight = ch + c.getBorderWidth("tb");
33296             if(g.maxHeight){
33297                 newHeight = Math.min(g.maxHeight, newHeight);
33298             }
33299             c.setHeight(newHeight);
33300         }
33301
33302         if(g.autoWidth){
33303             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
33304         }
33305
33306         var s = this.scroller;
33307
33308         var csize = c.getSize(true);
33309
33310         this.el.setSize(csize.width, csize.height);
33311
33312         this.headerPanel.setWidth(csize.width);
33313         this.footerPanel.setWidth(csize.width);
33314
33315         var hdHeight = this.mainHd.getHeight();
33316         var vw = csize.width;
33317         var vh = csize.height - (tbh + bbh);
33318
33319         s.setSize(vw, vh);
33320
33321         var bt = this.getBodyTable();
33322         var ltWidth = hasLock ?
33323                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
33324
33325         var scrollHeight = bt.offsetHeight;
33326         var scrollWidth = ltWidth + bt.offsetWidth;
33327         var vscroll = false, hscroll = false;
33328
33329         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
33330
33331         var lw = this.lockedWrap, mw = this.mainWrap;
33332         var lb = this.lockedBody, mb = this.mainBody;
33333
33334         setTimeout(function(){
33335             var t = s.dom.offsetTop;
33336             var w = s.dom.clientWidth,
33337                 h = s.dom.clientHeight;
33338
33339             lw.setTop(t);
33340             lw.setSize(ltWidth, h);
33341
33342             mw.setLeftTop(ltWidth, t);
33343             mw.setSize(w-ltWidth, h);
33344
33345             lb.setHeight(h-hdHeight);
33346             mb.setHeight(h-hdHeight);
33347
33348             if(is2ndPass !== true && !gv.userResized && expandCol){
33349                 // high speed resize without full column calculation
33350                 
33351                 var ci = cm.getIndexById(expandCol);
33352                 if (ci < 0) {
33353                     ci = cm.findColumnIndex(expandCol);
33354                 }
33355                 ci = Math.max(0, ci); // make sure it's got at least the first col.
33356                 var expandId = cm.getColumnId(ci);
33357                 var  tw = cm.getTotalWidth(false);
33358                 var currentWidth = cm.getColumnWidth(ci);
33359                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
33360                 if(currentWidth != cw){
33361                     cm.setColumnWidth(ci, cw, true);
33362                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33363                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33364                     gv.updateSplitters();
33365                     gv.layout(false, true);
33366                 }
33367             }
33368
33369             if(initialRender){
33370                 lw.show();
33371                 mw.show();
33372             }
33373             //c.endMeasure();
33374         }, 10);
33375     },
33376
33377     onWindowResize : function(){
33378         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
33379             return;
33380         }
33381         this.layout();
33382     },
33383
33384     appendFooter : function(parentEl){
33385         return null;
33386     },
33387
33388     sortAscText : "Sort Ascending",
33389     sortDescText : "Sort Descending",
33390     lockText : "Lock Column",
33391     unlockText : "Unlock Column",
33392     columnsText : "Columns"
33393 });
33394
33395
33396 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
33397     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
33398     this.proxy.el.addClass('x-grid3-col-dd');
33399 };
33400
33401 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
33402     handleMouseDown : function(e){
33403
33404     },
33405
33406     callHandleMouseDown : function(e){
33407         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
33408     }
33409 });
33410 /*
33411  * Based on:
33412  * Ext JS Library 1.1.1
33413  * Copyright(c) 2006-2007, Ext JS, LLC.
33414  *
33415  * Originally Released Under LGPL - original licence link has changed is not relivant.
33416  *
33417  * Fork - LGPL
33418  * <script type="text/javascript">
33419  */
33420  
33421 // private
33422 // This is a support class used internally by the Grid components
33423 Roo.grid.SplitDragZone = function(grid, hd, hd2){
33424     this.grid = grid;
33425     this.view = grid.getView();
33426     this.proxy = this.view.resizeProxy;
33427     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
33428         "gridSplitters" + this.grid.getGridEl().id, {
33429         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
33430     });
33431     this.setHandleElId(Roo.id(hd));
33432     this.setOuterHandleElId(Roo.id(hd2));
33433     this.scroll = false;
33434 };
33435 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
33436     fly: Roo.Element.fly,
33437
33438     b4StartDrag : function(x, y){
33439         this.view.headersDisabled = true;
33440         this.proxy.setHeight(this.view.mainWrap.getHeight());
33441         var w = this.cm.getColumnWidth(this.cellIndex);
33442         var minw = Math.max(w-this.grid.minColumnWidth, 0);
33443         this.resetConstraints();
33444         this.setXConstraint(minw, 1000);
33445         this.setYConstraint(0, 0);
33446         this.minX = x - minw;
33447         this.maxX = x + 1000;
33448         this.startPos = x;
33449         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
33450     },
33451
33452
33453     handleMouseDown : function(e){
33454         ev = Roo.EventObject.setEvent(e);
33455         var t = this.fly(ev.getTarget());
33456         if(t.hasClass("x-grid-split")){
33457             this.cellIndex = this.view.getCellIndex(t.dom);
33458             this.split = t.dom;
33459             this.cm = this.grid.colModel;
33460             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
33461                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
33462             }
33463         }
33464     },
33465
33466     endDrag : function(e){
33467         this.view.headersDisabled = false;
33468         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
33469         var diff = endX - this.startPos;
33470         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
33471     },
33472
33473     autoOffset : function(){
33474         this.setDelta(0,0);
33475     }
33476 });/*
33477  * Based on:
33478  * Ext JS Library 1.1.1
33479  * Copyright(c) 2006-2007, Ext JS, LLC.
33480  *
33481  * Originally Released Under LGPL - original licence link has changed is not relivant.
33482  *
33483  * Fork - LGPL
33484  * <script type="text/javascript">
33485  */
33486  
33487 // private
33488 // This is a support class used internally by the Grid components
33489 Roo.grid.GridDragZone = function(grid, config){
33490     this.view = grid.getView();
33491     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
33492     if(this.view.lockedBody){
33493         this.setHandleElId(Roo.id(this.view.mainBody.dom));
33494         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
33495     }
33496     this.scroll = false;
33497     this.grid = grid;
33498     this.ddel = document.createElement('div');
33499     this.ddel.className = 'x-grid-dd-wrap';
33500 };
33501
33502 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
33503     ddGroup : "GridDD",
33504
33505     getDragData : function(e){
33506         var t = Roo.lib.Event.getTarget(e);
33507         var rowIndex = this.view.findRowIndex(t);
33508         if(rowIndex !== false){
33509             var sm = this.grid.selModel;
33510             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
33511               //  sm.mouseDown(e, t);
33512             //}
33513             if (e.hasModifier()){
33514                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
33515             }
33516             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
33517         }
33518         return false;
33519     },
33520
33521     onInitDrag : function(e){
33522         var data = this.dragData;
33523         this.ddel.innerHTML = this.grid.getDragDropText();
33524         this.proxy.update(this.ddel);
33525         // fire start drag?
33526     },
33527
33528     afterRepair : function(){
33529         this.dragging = false;
33530     },
33531
33532     getRepairXY : function(e, data){
33533         return false;
33534     },
33535
33536     onEndDrag : function(data, e){
33537         // fire end drag?
33538     },
33539
33540     onValidDrop : function(dd, e, id){
33541         // fire drag drop?
33542         this.hideProxy();
33543     },
33544
33545     beforeInvalidDrop : function(e, id){
33546
33547     }
33548 });/*
33549  * Based on:
33550  * Ext JS Library 1.1.1
33551  * Copyright(c) 2006-2007, Ext JS, LLC.
33552  *
33553  * Originally Released Under LGPL - original licence link has changed is not relivant.
33554  *
33555  * Fork - LGPL
33556  * <script type="text/javascript">
33557  */
33558  
33559
33560 /**
33561  * @class Roo.grid.ColumnModel
33562  * @extends Roo.util.Observable
33563  * This is the default implementation of a ColumnModel used by the Grid. It defines
33564  * the columns in the grid.
33565  * <br>Usage:<br>
33566  <pre><code>
33567  var colModel = new Roo.grid.ColumnModel([
33568         {header: "Ticker", width: 60, sortable: true, locked: true},
33569         {header: "Company Name", width: 150, sortable: true},
33570         {header: "Market Cap.", width: 100, sortable: true},
33571         {header: "$ Sales", width: 100, sortable: true, renderer: money},
33572         {header: "Employees", width: 100, sortable: true, resizable: false}
33573  ]);
33574  </code></pre>
33575  * <p>
33576  
33577  * The config options listed for this class are options which may appear in each
33578  * individual column definition.
33579  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
33580  * @constructor
33581  * @param {Object} config An Array of column config objects. See this class's
33582  * config objects for details.
33583 */
33584 Roo.grid.ColumnModel = function(config){
33585         /**
33586      * The config passed into the constructor
33587      */
33588     this.config = config;
33589     this.lookup = {};
33590
33591     // if no id, create one
33592     // if the column does not have a dataIndex mapping,
33593     // map it to the order it is in the config
33594     for(var i = 0, len = config.length; i < len; i++){
33595         var c = config[i];
33596         if(typeof c.dataIndex == "undefined"){
33597             c.dataIndex = i;
33598         }
33599         if(typeof c.renderer == "string"){
33600             c.renderer = Roo.util.Format[c.renderer];
33601         }
33602         if(typeof c.id == "undefined"){
33603             c.id = Roo.id();
33604         }
33605         if(c.editor && c.editor.xtype){
33606             c.editor  = Roo.factory(c.editor, Roo.grid);
33607         }
33608         if(c.editor && c.editor.isFormField){
33609             c.editor = new Roo.grid.GridEditor(c.editor);
33610         }
33611         this.lookup[c.id] = c;
33612     }
33613
33614     /**
33615      * The width of columns which have no width specified (defaults to 100)
33616      * @type Number
33617      */
33618     this.defaultWidth = 100;
33619
33620     /**
33621      * Default sortable of columns which have no sortable specified (defaults to false)
33622      * @type Boolean
33623      */
33624     this.defaultSortable = false;
33625
33626     this.addEvents({
33627         /**
33628              * @event widthchange
33629              * Fires when the width of a column changes.
33630              * @param {ColumnModel} this
33631              * @param {Number} columnIndex The column index
33632              * @param {Number} newWidth The new width
33633              */
33634             "widthchange": true,
33635         /**
33636              * @event headerchange
33637              * Fires when the text of a header changes.
33638              * @param {ColumnModel} this
33639              * @param {Number} columnIndex The column index
33640              * @param {Number} newText The new header text
33641              */
33642             "headerchange": true,
33643         /**
33644              * @event hiddenchange
33645              * Fires when a column is hidden or "unhidden".
33646              * @param {ColumnModel} this
33647              * @param {Number} columnIndex The column index
33648              * @param {Boolean} hidden true if hidden, false otherwise
33649              */
33650             "hiddenchange": true,
33651             /**
33652          * @event columnmoved
33653          * Fires when a column is moved.
33654          * @param {ColumnModel} this
33655          * @param {Number} oldIndex
33656          * @param {Number} newIndex
33657          */
33658         "columnmoved" : true,
33659         /**
33660          * @event columlockchange
33661          * Fires when a column's locked state is changed
33662          * @param {ColumnModel} this
33663          * @param {Number} colIndex
33664          * @param {Boolean} locked true if locked
33665          */
33666         "columnlockchange" : true
33667     });
33668     Roo.grid.ColumnModel.superclass.constructor.call(this);
33669 };
33670 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
33671     /**
33672      * @cfg {String} header The header text to display in the Grid view.
33673      */
33674     /**
33675      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
33676      * {@link Roo.data.Record} definition from which to draw the column's value. If not
33677      * specified, the column's index is used as an index into the Record's data Array.
33678      */
33679     /**
33680      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
33681      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
33682      */
33683     /**
33684      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
33685      * Defaults to the value of the {@link #defaultSortable} property.
33686      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
33687      */
33688     /**
33689      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
33690      */
33691     /**
33692      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
33693      */
33694     /**
33695      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
33696      */
33697     /**
33698      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
33699      */
33700     /**
33701      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
33702      * given the cell's data value. See {@link #setRenderer}. If not specified, the
33703      * default renderer uses the raw data value.
33704      */
33705        /**
33706      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
33707      */
33708     /**
33709      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
33710      */
33711
33712     /**
33713      * Returns the id of the column at the specified index.
33714      * @param {Number} index The column index
33715      * @return {String} the id
33716      */
33717     getColumnId : function(index){
33718         return this.config[index].id;
33719     },
33720
33721     /**
33722      * Returns the column for a specified id.
33723      * @param {String} id The column id
33724      * @return {Object} the column
33725      */
33726     getColumnById : function(id){
33727         return this.lookup[id];
33728     },
33729
33730     /**
33731      * Returns the index for a specified column id.
33732      * @param {String} id The column id
33733      * @return {Number} the index, or -1 if not found
33734      */
33735     getIndexById : function(id){
33736         for(var i = 0, len = this.config.length; i < len; i++){
33737             if(this.config[i].id == id){
33738                 return i;
33739             }
33740         }
33741         return -1;
33742     },
33743     /**
33744      * Returns the index for a specified column dataIndex.
33745      * @param {String} dataIndex The column dataIndex
33746      * @return {Number} the index, or -1 if not found
33747      */
33748     
33749     findColumnIndex : function(dataIndex){
33750         for(var i = 0, len = this.config.length; i < len; i++){
33751             if(this.config[i].dataIndex == dataIndex){
33752                 return i;
33753             }
33754         }
33755         return -1;
33756     },
33757     
33758     
33759     moveColumn : function(oldIndex, newIndex){
33760         var c = this.config[oldIndex];
33761         this.config.splice(oldIndex, 1);
33762         this.config.splice(newIndex, 0, c);
33763         this.dataMap = null;
33764         this.fireEvent("columnmoved", this, oldIndex, newIndex);
33765     },
33766
33767     isLocked : function(colIndex){
33768         return this.config[colIndex].locked === true;
33769     },
33770
33771     setLocked : function(colIndex, value, suppressEvent){
33772         if(this.isLocked(colIndex) == value){
33773             return;
33774         }
33775         this.config[colIndex].locked = value;
33776         if(!suppressEvent){
33777             this.fireEvent("columnlockchange", this, colIndex, value);
33778         }
33779     },
33780
33781     getTotalLockedWidth : function(){
33782         var totalWidth = 0;
33783         for(var i = 0; i < this.config.length; i++){
33784             if(this.isLocked(i) && !this.isHidden(i)){
33785                 this.totalWidth += this.getColumnWidth(i);
33786             }
33787         }
33788         return totalWidth;
33789     },
33790
33791     getLockedCount : function(){
33792         for(var i = 0, len = this.config.length; i < len; i++){
33793             if(!this.isLocked(i)){
33794                 return i;
33795             }
33796         }
33797     },
33798
33799     /**
33800      * Returns the number of columns.
33801      * @return {Number}
33802      */
33803     getColumnCount : function(visibleOnly){
33804         if(visibleOnly === true){
33805             var c = 0;
33806             for(var i = 0, len = this.config.length; i < len; i++){
33807                 if(!this.isHidden(i)){
33808                     c++;
33809                 }
33810             }
33811             return c;
33812         }
33813         return this.config.length;
33814     },
33815
33816     /**
33817      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
33818      * @param {Function} fn
33819      * @param {Object} scope (optional)
33820      * @return {Array} result
33821      */
33822     getColumnsBy : function(fn, scope){
33823         var r = [];
33824         for(var i = 0, len = this.config.length; i < len; i++){
33825             var c = this.config[i];
33826             if(fn.call(scope||this, c, i) === true){
33827                 r[r.length] = c;
33828             }
33829         }
33830         return r;
33831     },
33832
33833     /**
33834      * Returns true if the specified column is sortable.
33835      * @param {Number} col The column index
33836      * @return {Boolean}
33837      */
33838     isSortable : function(col){
33839         if(typeof this.config[col].sortable == "undefined"){
33840             return this.defaultSortable;
33841         }
33842         return this.config[col].sortable;
33843     },
33844
33845     /**
33846      * Returns the rendering (formatting) function defined for the column.
33847      * @param {Number} col The column index.
33848      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
33849      */
33850     getRenderer : function(col){
33851         if(!this.config[col].renderer){
33852             return Roo.grid.ColumnModel.defaultRenderer;
33853         }
33854         return this.config[col].renderer;
33855     },
33856
33857     /**
33858      * Sets the rendering (formatting) function for a column.
33859      * @param {Number} col The column index
33860      * @param {Function} fn The function to use to process the cell's raw data
33861      * to return HTML markup for the grid view. The render function is called with
33862      * the following parameters:<ul>
33863      * <li>Data value.</li>
33864      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
33865      * <li>css A CSS style string to apply to the table cell.</li>
33866      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
33867      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
33868      * <li>Row index</li>
33869      * <li>Column index</li>
33870      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
33871      */
33872     setRenderer : function(col, fn){
33873         this.config[col].renderer = fn;
33874     },
33875
33876     /**
33877      * Returns the width for the specified column.
33878      * @param {Number} col The column index
33879      * @return {Number}
33880      */
33881     getColumnWidth : function(col){
33882         return this.config[col].width || this.defaultWidth;
33883     },
33884
33885     /**
33886      * Sets the width for a column.
33887      * @param {Number} col The column index
33888      * @param {Number} width The new width
33889      */
33890     setColumnWidth : function(col, width, suppressEvent){
33891         this.config[col].width = width;
33892         this.totalWidth = null;
33893         if(!suppressEvent){
33894              this.fireEvent("widthchange", this, col, width);
33895         }
33896     },
33897
33898     /**
33899      * Returns the total width of all columns.
33900      * @param {Boolean} includeHidden True to include hidden column widths
33901      * @return {Number}
33902      */
33903     getTotalWidth : function(includeHidden){
33904         if(!this.totalWidth){
33905             this.totalWidth = 0;
33906             for(var i = 0, len = this.config.length; i < len; i++){
33907                 if(includeHidden || !this.isHidden(i)){
33908                     this.totalWidth += this.getColumnWidth(i);
33909                 }
33910             }
33911         }
33912         return this.totalWidth;
33913     },
33914
33915     /**
33916      * Returns the header for the specified column.
33917      * @param {Number} col The column index
33918      * @return {String}
33919      */
33920     getColumnHeader : function(col){
33921         return this.config[col].header;
33922     },
33923
33924     /**
33925      * Sets the header for a column.
33926      * @param {Number} col The column index
33927      * @param {String} header The new header
33928      */
33929     setColumnHeader : function(col, header){
33930         this.config[col].header = header;
33931         this.fireEvent("headerchange", this, col, header);
33932     },
33933
33934     /**
33935      * Returns the tooltip for the specified column.
33936      * @param {Number} col The column index
33937      * @return {String}
33938      */
33939     getColumnTooltip : function(col){
33940             return this.config[col].tooltip;
33941     },
33942     /**
33943      * Sets the tooltip for a column.
33944      * @param {Number} col The column index
33945      * @param {String} tooltip The new tooltip
33946      */
33947     setColumnTooltip : function(col, tooltip){
33948             this.config[col].tooltip = tooltip;
33949     },
33950
33951     /**
33952      * Returns the dataIndex for the specified column.
33953      * @param {Number} col The column index
33954      * @return {Number}
33955      */
33956     getDataIndex : function(col){
33957         return this.config[col].dataIndex;
33958     },
33959
33960     /**
33961      * Sets the dataIndex for a column.
33962      * @param {Number} col The column index
33963      * @param {Number} dataIndex The new dataIndex
33964      */
33965     setDataIndex : function(col, dataIndex){
33966         this.config[col].dataIndex = dataIndex;
33967     },
33968
33969     
33970     
33971     /**
33972      * Returns true if the cell is editable.
33973      * @param {Number} colIndex The column index
33974      * @param {Number} rowIndex The row index
33975      * @return {Boolean}
33976      */
33977     isCellEditable : function(colIndex, rowIndex){
33978         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
33979     },
33980
33981     /**
33982      * Returns the editor defined for the cell/column.
33983      * return false or null to disable editing.
33984      * @param {Number} colIndex The column index
33985      * @param {Number} rowIndex The row index
33986      * @return {Object}
33987      */
33988     getCellEditor : function(colIndex, rowIndex){
33989         return this.config[colIndex].editor;
33990     },
33991
33992     /**
33993      * Sets if a column is editable.
33994      * @param {Number} col The column index
33995      * @param {Boolean} editable True if the column is editable
33996      */
33997     setEditable : function(col, editable){
33998         this.config[col].editable = editable;
33999     },
34000
34001
34002     /**
34003      * Returns true if the column is hidden.
34004      * @param {Number} colIndex The column index
34005      * @return {Boolean}
34006      */
34007     isHidden : function(colIndex){
34008         return this.config[colIndex].hidden;
34009     },
34010
34011
34012     /**
34013      * Returns true if the column width cannot be changed
34014      */
34015     isFixed : function(colIndex){
34016         return this.config[colIndex].fixed;
34017     },
34018
34019     /**
34020      * Returns true if the column can be resized
34021      * @return {Boolean}
34022      */
34023     isResizable : function(colIndex){
34024         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34025     },
34026     /**
34027      * Sets if a column is hidden.
34028      * @param {Number} colIndex The column index
34029      * @param {Boolean} hidden True if the column is hidden
34030      */
34031     setHidden : function(colIndex, hidden){
34032         this.config[colIndex].hidden = hidden;
34033         this.totalWidth = null;
34034         this.fireEvent("hiddenchange", this, colIndex, hidden);
34035     },
34036
34037     /**
34038      * Sets the editor for a column.
34039      * @param {Number} col The column index
34040      * @param {Object} editor The editor object
34041      */
34042     setEditor : function(col, editor){
34043         this.config[col].editor = editor;
34044     }
34045 });
34046
34047 Roo.grid.ColumnModel.defaultRenderer = function(value){
34048         if(typeof value == "string" && value.length < 1){
34049             return "&#160;";
34050         }
34051         return value;
34052 };
34053
34054 // Alias for backwards compatibility
34055 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34056 /*
34057  * Based on:
34058  * Ext JS Library 1.1.1
34059  * Copyright(c) 2006-2007, Ext JS, LLC.
34060  *
34061  * Originally Released Under LGPL - original licence link has changed is not relivant.
34062  *
34063  * Fork - LGPL
34064  * <script type="text/javascript">
34065  */
34066
34067 /**
34068  * @class Roo.grid.AbstractSelectionModel
34069  * @extends Roo.util.Observable
34070  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34071  * implemented by descendant classes.  This class should not be directly instantiated.
34072  * @constructor
34073  */
34074 Roo.grid.AbstractSelectionModel = function(){
34075     this.locked = false;
34076     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34077 };
34078
34079 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34080     /** @ignore Called by the grid automatically. Do not call directly. */
34081     init : function(grid){
34082         this.grid = grid;
34083         this.initEvents();
34084     },
34085
34086     /**
34087      * Locks the selections.
34088      */
34089     lock : function(){
34090         this.locked = true;
34091     },
34092
34093     /**
34094      * Unlocks the selections.
34095      */
34096     unlock : function(){
34097         this.locked = false;
34098     },
34099
34100     /**
34101      * Returns true if the selections are locked.
34102      * @return {Boolean}
34103      */
34104     isLocked : function(){
34105         return this.locked;
34106     }
34107 });/*
34108  * Based on:
34109  * Ext JS Library 1.1.1
34110  * Copyright(c) 2006-2007, Ext JS, LLC.
34111  *
34112  * Originally Released Under LGPL - original licence link has changed is not relivant.
34113  *
34114  * Fork - LGPL
34115  * <script type="text/javascript">
34116  */
34117 /**
34118  * @extends Roo.grid.AbstractSelectionModel
34119  * @class Roo.grid.RowSelectionModel
34120  * The default SelectionModel used by {@link Roo.grid.Grid}.
34121  * It supports multiple selections and keyboard selection/navigation. 
34122  * @constructor
34123  * @param {Object} config
34124  */
34125 Roo.grid.RowSelectionModel = function(config){
34126     Roo.apply(this, config);
34127     this.selections = new Roo.util.MixedCollection(false, function(o){
34128         return o.id;
34129     });
34130
34131     this.last = false;
34132     this.lastActive = false;
34133
34134     this.addEvents({
34135         /**
34136              * @event selectionchange
34137              * Fires when the selection changes
34138              * @param {SelectionModel} this
34139              */
34140             "selectionchange" : true,
34141         /**
34142              * @event afterselectionchange
34143              * Fires after the selection changes (eg. by key press or clicking)
34144              * @param {SelectionModel} this
34145              */
34146             "afterselectionchange" : true,
34147         /**
34148              * @event beforerowselect
34149              * Fires when a row is selected being selected, return false to cancel.
34150              * @param {SelectionModel} this
34151              * @param {Number} rowIndex The selected index
34152              * @param {Boolean} keepExisting False if other selections will be cleared
34153              */
34154             "beforerowselect" : true,
34155         /**
34156              * @event rowselect
34157              * Fires when a row is selected.
34158              * @param {SelectionModel} this
34159              * @param {Number} rowIndex The selected index
34160              * @param {Roo.data.Record} r The record
34161              */
34162             "rowselect" : true,
34163         /**
34164              * @event rowdeselect
34165              * Fires when a row is deselected.
34166              * @param {SelectionModel} this
34167              * @param {Number} rowIndex The selected index
34168              */
34169         "rowdeselect" : true
34170     });
34171     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
34172     this.locked = false;
34173 };
34174
34175 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
34176     /**
34177      * @cfg {Boolean} singleSelect
34178      * True to allow selection of only one row at a time (defaults to false)
34179      */
34180     singleSelect : false,
34181
34182     // private
34183     initEvents : function(){
34184
34185         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
34186             this.grid.on("mousedown", this.handleMouseDown, this);
34187         }else{ // allow click to work like normal
34188             this.grid.on("rowclick", this.handleDragableRowClick, this);
34189         }
34190
34191         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
34192             "up" : function(e){
34193                 if(!e.shiftKey){
34194                     this.selectPrevious(e.shiftKey);
34195                 }else if(this.last !== false && this.lastActive !== false){
34196                     var last = this.last;
34197                     this.selectRange(this.last,  this.lastActive-1);
34198                     this.grid.getView().focusRow(this.lastActive);
34199                     if(last !== false){
34200                         this.last = last;
34201                     }
34202                 }else{
34203                     this.selectFirstRow();
34204                 }
34205                 this.fireEvent("afterselectionchange", this);
34206             },
34207             "down" : function(e){
34208                 if(!e.shiftKey){
34209                     this.selectNext(e.shiftKey);
34210                 }else if(this.last !== false && this.lastActive !== false){
34211                     var last = this.last;
34212                     this.selectRange(this.last,  this.lastActive+1);
34213                     this.grid.getView().focusRow(this.lastActive);
34214                     if(last !== false){
34215                         this.last = last;
34216                     }
34217                 }else{
34218                     this.selectFirstRow();
34219                 }
34220                 this.fireEvent("afterselectionchange", this);
34221             },
34222             scope: this
34223         });
34224
34225         var view = this.grid.view;
34226         view.on("refresh", this.onRefresh, this);
34227         view.on("rowupdated", this.onRowUpdated, this);
34228         view.on("rowremoved", this.onRemove, this);
34229     },
34230
34231     // private
34232     onRefresh : function(){
34233         var ds = this.grid.dataSource, i, v = this.grid.view;
34234         var s = this.selections;
34235         s.each(function(r){
34236             if((i = ds.indexOfId(r.id)) != -1){
34237                 v.onRowSelect(i);
34238             }else{
34239                 s.remove(r);
34240             }
34241         });
34242     },
34243
34244     // private
34245     onRemove : function(v, index, r){
34246         this.selections.remove(r);
34247     },
34248
34249     // private
34250     onRowUpdated : function(v, index, r){
34251         if(this.isSelected(r)){
34252             v.onRowSelect(index);
34253         }
34254     },
34255
34256     /**
34257      * Select records.
34258      * @param {Array} records The records to select
34259      * @param {Boolean} keepExisting (optional) True to keep existing selections
34260      */
34261     selectRecords : function(records, keepExisting){
34262         if(!keepExisting){
34263             this.clearSelections();
34264         }
34265         var ds = this.grid.dataSource;
34266         for(var i = 0, len = records.length; i < len; i++){
34267             this.selectRow(ds.indexOf(records[i]), true);
34268         }
34269     },
34270
34271     /**
34272      * Gets the number of selected rows.
34273      * @return {Number}
34274      */
34275     getCount : function(){
34276         return this.selections.length;
34277     },
34278
34279     /**
34280      * Selects the first row in the grid.
34281      */
34282     selectFirstRow : function(){
34283         this.selectRow(0);
34284     },
34285
34286     /**
34287      * Select the last row.
34288      * @param {Boolean} keepExisting (optional) True to keep existing selections
34289      */
34290     selectLastRow : function(keepExisting){
34291         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
34292     },
34293
34294     /**
34295      * Selects the row immediately following the last selected row.
34296      * @param {Boolean} keepExisting (optional) True to keep existing selections
34297      */
34298     selectNext : function(keepExisting){
34299         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
34300             this.selectRow(this.last+1, keepExisting);
34301             this.grid.getView().focusRow(this.last);
34302         }
34303     },
34304
34305     /**
34306      * Selects the row that precedes the last selected row.
34307      * @param {Boolean} keepExisting (optional) True to keep existing selections
34308      */
34309     selectPrevious : function(keepExisting){
34310         if(this.last){
34311             this.selectRow(this.last-1, keepExisting);
34312             this.grid.getView().focusRow(this.last);
34313         }
34314     },
34315
34316     /**
34317      * Returns the selected records
34318      * @return {Array} Array of selected records
34319      */
34320     getSelections : function(){
34321         return [].concat(this.selections.items);
34322     },
34323
34324     /**
34325      * Returns the first selected record.
34326      * @return {Record}
34327      */
34328     getSelected : function(){
34329         return this.selections.itemAt(0);
34330     },
34331
34332
34333     /**
34334      * Clears all selections.
34335      */
34336     clearSelections : function(fast){
34337         if(this.locked) return;
34338         if(fast !== true){
34339             var ds = this.grid.dataSource;
34340             var s = this.selections;
34341             s.each(function(r){
34342                 this.deselectRow(ds.indexOfId(r.id));
34343             }, this);
34344             s.clear();
34345         }else{
34346             this.selections.clear();
34347         }
34348         this.last = false;
34349     },
34350
34351
34352     /**
34353      * Selects all rows.
34354      */
34355     selectAll : function(){
34356         if(this.locked) return;
34357         this.selections.clear();
34358         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
34359             this.selectRow(i, true);
34360         }
34361     },
34362
34363     /**
34364      * Returns True if there is a selection.
34365      * @return {Boolean}
34366      */
34367     hasSelection : function(){
34368         return this.selections.length > 0;
34369     },
34370
34371     /**
34372      * Returns True if the specified row is selected.
34373      * @param {Number/Record} record The record or index of the record to check
34374      * @return {Boolean}
34375      */
34376     isSelected : function(index){
34377         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
34378         return (r && this.selections.key(r.id) ? true : false);
34379     },
34380
34381     /**
34382      * Returns True if the specified record id is selected.
34383      * @param {String} id The id of record to check
34384      * @return {Boolean}
34385      */
34386     isIdSelected : function(id){
34387         return (this.selections.key(id) ? true : false);
34388     },
34389
34390     // private
34391     handleMouseDown : function(e, t){
34392         var view = this.grid.getView(), rowIndex;
34393         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
34394             return;
34395         };
34396         if(e.shiftKey && this.last !== false){
34397             var last = this.last;
34398             this.selectRange(last, rowIndex, e.ctrlKey);
34399             this.last = last; // reset the last
34400             view.focusRow(rowIndex);
34401         }else{
34402             var isSelected = this.isSelected(rowIndex);
34403             if(e.button !== 0 && isSelected){
34404                 view.focusRow(rowIndex);
34405             }else if(e.ctrlKey && isSelected){
34406                 this.deselectRow(rowIndex);
34407             }else if(!isSelected){
34408                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
34409                 view.focusRow(rowIndex);
34410             }
34411         }
34412         this.fireEvent("afterselectionchange", this);
34413     },
34414     // private
34415     handleDragableRowClick :  function(grid, rowIndex, e) 
34416     {
34417         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
34418             this.selectRow(rowIndex, false);
34419             grid.view.focusRow(rowIndex);
34420              this.fireEvent("afterselectionchange", this);
34421         }
34422     },
34423     
34424     /**
34425      * Selects multiple rows.
34426      * @param {Array} rows Array of the indexes of the row to select
34427      * @param {Boolean} keepExisting (optional) True to keep existing selections
34428      */
34429     selectRows : function(rows, keepExisting){
34430         if(!keepExisting){
34431             this.clearSelections();
34432         }
34433         for(var i = 0, len = rows.length; i < len; i++){
34434             this.selectRow(rows[i], true);
34435         }
34436     },
34437
34438     /**
34439      * Selects a range of rows. All rows in between startRow and endRow are also selected.
34440      * @param {Number} startRow The index of the first row in the range
34441      * @param {Number} endRow The index of the last row in the range
34442      * @param {Boolean} keepExisting (optional) True to retain existing selections
34443      */
34444     selectRange : function(startRow, endRow, keepExisting){
34445         if(this.locked) return;
34446         if(!keepExisting){
34447             this.clearSelections();
34448         }
34449         if(startRow <= endRow){
34450             for(var i = startRow; i <= endRow; i++){
34451                 this.selectRow(i, true);
34452             }
34453         }else{
34454             for(var i = startRow; i >= endRow; i--){
34455                 this.selectRow(i, true);
34456             }
34457         }
34458     },
34459
34460     /**
34461      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
34462      * @param {Number} startRow The index of the first row in the range
34463      * @param {Number} endRow The index of the last row in the range
34464      */
34465     deselectRange : function(startRow, endRow, preventViewNotify){
34466         if(this.locked) return;
34467         for(var i = startRow; i <= endRow; i++){
34468             this.deselectRow(i, preventViewNotify);
34469         }
34470     },
34471
34472     /**
34473      * Selects a row.
34474      * @param {Number} row The index of the row to select
34475      * @param {Boolean} keepExisting (optional) True to keep existing selections
34476      */
34477     selectRow : function(index, keepExisting, preventViewNotify){
34478         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
34479         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
34480             if(!keepExisting || this.singleSelect){
34481                 this.clearSelections();
34482             }
34483             var r = this.grid.dataSource.getAt(index);
34484             this.selections.add(r);
34485             this.last = this.lastActive = index;
34486             if(!preventViewNotify){
34487                 this.grid.getView().onRowSelect(index);
34488             }
34489             this.fireEvent("rowselect", this, index, r);
34490             this.fireEvent("selectionchange", this);
34491         }
34492     },
34493
34494     /**
34495      * Deselects a row.
34496      * @param {Number} row The index of the row to deselect
34497      */
34498     deselectRow : function(index, preventViewNotify){
34499         if(this.locked) return;
34500         if(this.last == index){
34501             this.last = false;
34502         }
34503         if(this.lastActive == index){
34504             this.lastActive = false;
34505         }
34506         var r = this.grid.dataSource.getAt(index);
34507         this.selections.remove(r);
34508         if(!preventViewNotify){
34509             this.grid.getView().onRowDeselect(index);
34510         }
34511         this.fireEvent("rowdeselect", this, index);
34512         this.fireEvent("selectionchange", this);
34513     },
34514
34515     // private
34516     restoreLast : function(){
34517         if(this._last){
34518             this.last = this._last;
34519         }
34520     },
34521
34522     // private
34523     acceptsNav : function(row, col, cm){
34524         return !cm.isHidden(col) && cm.isCellEditable(col, row);
34525     },
34526
34527     // private
34528     onEditorKey : function(field, e){
34529         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
34530         if(k == e.TAB){
34531             e.stopEvent();
34532             ed.completeEdit();
34533             if(e.shiftKey){
34534                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
34535             }else{
34536                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
34537             }
34538         }else if(k == e.ENTER && !e.ctrlKey){
34539             e.stopEvent();
34540             ed.completeEdit();
34541             if(e.shiftKey){
34542                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
34543             }else{
34544                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
34545             }
34546         }else if(k == e.ESC){
34547             ed.cancelEdit();
34548         }
34549         if(newCell){
34550             g.startEditing(newCell[0], newCell[1]);
34551         }
34552     }
34553 });/*
34554  * Based on:
34555  * Ext JS Library 1.1.1
34556  * Copyright(c) 2006-2007, Ext JS, LLC.
34557  *
34558  * Originally Released Under LGPL - original licence link has changed is not relivant.
34559  *
34560  * Fork - LGPL
34561  * <script type="text/javascript">
34562  */
34563 /**
34564  * @class Roo.grid.CellSelectionModel
34565  * @extends Roo.grid.AbstractSelectionModel
34566  * This class provides the basic implementation for cell selection in a grid.
34567  * @constructor
34568  * @param {Object} config The object containing the configuration of this model.
34569  */
34570 Roo.grid.CellSelectionModel = function(config){
34571     Roo.apply(this, config);
34572
34573     this.selection = null;
34574
34575     this.addEvents({
34576         /**
34577              * @event beforerowselect
34578              * Fires before a cell is selected.
34579              * @param {SelectionModel} this
34580              * @param {Number} rowIndex The selected row index
34581              * @param {Number} colIndex The selected cell index
34582              */
34583             "beforecellselect" : true,
34584         /**
34585              * @event cellselect
34586              * Fires when a cell is selected.
34587              * @param {SelectionModel} this
34588              * @param {Number} rowIndex The selected row index
34589              * @param {Number} colIndex The selected cell index
34590              */
34591             "cellselect" : true,
34592         /**
34593              * @event selectionchange
34594              * Fires when the active selection changes.
34595              * @param {SelectionModel} this
34596              * @param {Object} selection null for no selection or an object (o) with two properties
34597                 <ul>
34598                 <li>o.record: the record object for the row the selection is in</li>
34599                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
34600                 </ul>
34601              */
34602             "selectionchange" : true
34603     });
34604     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
34605 };
34606
34607 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
34608
34609     /** @ignore */
34610     initEvents : function(){
34611         this.grid.on("mousedown", this.handleMouseDown, this);
34612         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
34613         var view = this.grid.view;
34614         view.on("refresh", this.onViewChange, this);
34615         view.on("rowupdated", this.onRowUpdated, this);
34616         view.on("beforerowremoved", this.clearSelections, this);
34617         view.on("beforerowsinserted", this.clearSelections, this);
34618         if(this.grid.isEditor){
34619             this.grid.on("beforeedit", this.beforeEdit,  this);
34620         }
34621     },
34622
34623         //private
34624     beforeEdit : function(e){
34625         this.select(e.row, e.column, false, true, e.record);
34626     },
34627
34628         //private
34629     onRowUpdated : function(v, index, r){
34630         if(this.selection && this.selection.record == r){
34631             v.onCellSelect(index, this.selection.cell[1]);
34632         }
34633     },
34634
34635         //private
34636     onViewChange : function(){
34637         this.clearSelections(true);
34638     },
34639
34640         /**
34641          * Returns the currently selected cell,.
34642          * @return {Array} The selected cell (row, column) or null if none selected.
34643          */
34644     getSelectedCell : function(){
34645         return this.selection ? this.selection.cell : null;
34646     },
34647
34648     /**
34649      * Clears all selections.
34650      * @param {Boolean} true to prevent the gridview from being notified about the change.
34651      */
34652     clearSelections : function(preventNotify){
34653         var s = this.selection;
34654         if(s){
34655             if(preventNotify !== true){
34656                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
34657             }
34658             this.selection = null;
34659             this.fireEvent("selectionchange", this, null);
34660         }
34661     },
34662
34663     /**
34664      * Returns true if there is a selection.
34665      * @return {Boolean}
34666      */
34667     hasSelection : function(){
34668         return this.selection ? true : false;
34669     },
34670
34671     /** @ignore */
34672     handleMouseDown : function(e, t){
34673         var v = this.grid.getView();
34674         if(this.isLocked()){
34675             return;
34676         };
34677         var row = v.findRowIndex(t);
34678         var cell = v.findCellIndex(t);
34679         if(row !== false && cell !== false){
34680             this.select(row, cell);
34681         }
34682     },
34683
34684     /**
34685      * Selects a cell.
34686      * @param {Number} rowIndex
34687      * @param {Number} collIndex
34688      */
34689     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
34690         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
34691             this.clearSelections();
34692             r = r || this.grid.dataSource.getAt(rowIndex);
34693             this.selection = {
34694                 record : r,
34695                 cell : [rowIndex, colIndex]
34696             };
34697             if(!preventViewNotify){
34698                 var v = this.grid.getView();
34699                 v.onCellSelect(rowIndex, colIndex);
34700                 if(preventFocus !== true){
34701                     v.focusCell(rowIndex, colIndex);
34702                 }
34703             }
34704             this.fireEvent("cellselect", this, rowIndex, colIndex);
34705             this.fireEvent("selectionchange", this, this.selection);
34706         }
34707     },
34708
34709         //private
34710     isSelectable : function(rowIndex, colIndex, cm){
34711         return !cm.isHidden(colIndex);
34712     },
34713
34714     /** @ignore */
34715     handleKeyDown : function(e){
34716         if(!e.isNavKeyPress()){
34717             return;
34718         }
34719         var g = this.grid, s = this.selection;
34720         if(!s){
34721             e.stopEvent();
34722             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
34723             if(cell){
34724                 this.select(cell[0], cell[1]);
34725             }
34726             return;
34727         }
34728         var sm = this;
34729         var walk = function(row, col, step){
34730             return g.walkCells(row, col, step, sm.isSelectable,  sm);
34731         };
34732         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
34733         var newCell;
34734
34735         switch(k){
34736              case e.TAB:
34737                  if(e.shiftKey){
34738                      newCell = walk(r, c-1, -1);
34739                  }else{
34740                      newCell = walk(r, c+1, 1);
34741                  }
34742              break;
34743              case e.DOWN:
34744                  newCell = walk(r+1, c, 1);
34745              break;
34746              case e.UP:
34747                  newCell = walk(r-1, c, -1);
34748              break;
34749              case e.RIGHT:
34750                  newCell = walk(r, c+1, 1);
34751              break;
34752              case e.LEFT:
34753                  newCell = walk(r, c-1, -1);
34754              break;
34755              case e.ENTER:
34756                  if(g.isEditor && !g.editing){
34757                     g.startEditing(r, c);
34758                     e.stopEvent();
34759                     return;
34760                 }
34761              break;
34762         };
34763         if(newCell){
34764             this.select(newCell[0], newCell[1]);
34765             e.stopEvent();
34766         }
34767     },
34768
34769     acceptsNav : function(row, col, cm){
34770         return !cm.isHidden(col) && cm.isCellEditable(col, row);
34771     },
34772
34773     onEditorKey : function(field, e){
34774         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
34775         if(k == e.TAB){
34776             if(e.shiftKey){
34777                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
34778             }else{
34779                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
34780             }
34781             e.stopEvent();
34782         }else if(k == e.ENTER && !e.ctrlKey){
34783             ed.completeEdit();
34784             e.stopEvent();
34785         }else if(k == e.ESC){
34786             ed.cancelEdit();
34787         }
34788         if(newCell){
34789             g.startEditing(newCell[0], newCell[1]);
34790         }
34791     }
34792 });/*
34793  * Based on:
34794  * Ext JS Library 1.1.1
34795  * Copyright(c) 2006-2007, Ext JS, LLC.
34796  *
34797  * Originally Released Under LGPL - original licence link has changed is not relivant.
34798  *
34799  * Fork - LGPL
34800  * <script type="text/javascript">
34801  */
34802  
34803 /**
34804  * @class Roo.grid.EditorGrid
34805  * @extends Roo.grid.Grid
34806  * Class for creating and editable grid.
34807  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
34808  * The container MUST have some type of size defined for the grid to fill. The container will be 
34809  * automatically set to position relative if it isn't already.
34810  * @param {Object} dataSource The data model to bind to
34811  * @param {Object} colModel The column model with info about this grid's columns
34812  */
34813 Roo.grid.EditorGrid = function(container, config){
34814     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
34815     this.getGridEl().addClass("xedit-grid");
34816
34817     if(!this.selModel){
34818         this.selModel = new Roo.grid.CellSelectionModel();
34819     }
34820
34821     this.activeEditor = null;
34822
34823         this.addEvents({
34824             /**
34825              * @event beforeedit
34826              * Fires before cell editing is triggered. The edit event object has the following properties <br />
34827              * <ul style="padding:5px;padding-left:16px;">
34828              * <li>grid - This grid</li>
34829              * <li>record - The record being edited</li>
34830              * <li>field - The field name being edited</li>
34831              * <li>value - The value for the field being edited.</li>
34832              * <li>row - The grid row index</li>
34833              * <li>column - The grid column index</li>
34834              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
34835              * </ul>
34836              * @param {Object} e An edit event (see above for description)
34837              */
34838             "beforeedit" : true,
34839             /**
34840              * @event afteredit
34841              * Fires after a cell is edited. <br />
34842              * <ul style="padding:5px;padding-left:16px;">
34843              * <li>grid - This grid</li>
34844              * <li>record - The record being edited</li>
34845              * <li>field - The field name being edited</li>
34846              * <li>value - The value being set</li>
34847              * <li>originalValue - The original value for the field, before the edit.</li>
34848              * <li>row - The grid row index</li>
34849              * <li>column - The grid column index</li>
34850              * </ul>
34851              * @param {Object} e An edit event (see above for description)
34852              */
34853             "afteredit" : true,
34854             /**
34855              * @event validateedit
34856              * Fires after a cell is edited, but before the value is set in the record. 
34857          * You can use this to modify the value being set in the field, Return false
34858              * to cancel the change. The edit event object has the following properties <br />
34859              * <ul style="padding:5px;padding-left:16px;">
34860          * <li>editor - This editor</li>
34861              * <li>grid - This grid</li>
34862              * <li>record - The record being edited</li>
34863              * <li>field - The field name being edited</li>
34864              * <li>value - The value being set</li>
34865              * <li>originalValue - The original value for the field, before the edit.</li>
34866              * <li>row - The grid row index</li>
34867              * <li>column - The grid column index</li>
34868              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
34869              * </ul>
34870              * @param {Object} e An edit event (see above for description)
34871              */
34872             "validateedit" : true
34873         });
34874     this.on("bodyscroll", this.stopEditing,  this);
34875     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
34876 };
34877
34878 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
34879     /**
34880      * @cfg {Number} clicksToEdit
34881      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
34882      */
34883     clicksToEdit: 2,
34884
34885     // private
34886     isEditor : true,
34887     // private
34888     trackMouseOver: false, // causes very odd FF errors
34889
34890     onCellDblClick : function(g, row, col){
34891         this.startEditing(row, col);
34892     },
34893
34894     onEditComplete : function(ed, value, startValue){
34895         this.editing = false;
34896         this.activeEditor = null;
34897         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
34898         var r = ed.record;
34899         var field = this.colModel.getDataIndex(ed.col);
34900         var e = {
34901             grid: this,
34902             record: r,
34903             field: field,
34904             originalValue: startValue,
34905             value: value,
34906             row: ed.row,
34907             column: ed.col,
34908             cancel:false,
34909             editor: ed
34910         };
34911         if(String(value) !== String(startValue)){
34912             
34913             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
34914                 r.set(field, e.value);
34915                 delete e.cancel; //?? why!!!
34916                 this.fireEvent("afteredit", e);
34917             }
34918         } else {
34919             this.fireEvent("afteredit", e); // always fir it!
34920         }
34921         this.view.focusCell(ed.row, ed.col);
34922     },
34923
34924     /**
34925      * Starts editing the specified for the specified row/column
34926      * @param {Number} rowIndex
34927      * @param {Number} colIndex
34928      */
34929     startEditing : function(row, col){
34930         this.stopEditing();
34931         if(this.colModel.isCellEditable(col, row)){
34932             this.view.ensureVisible(row, col, true);
34933             var r = this.dataSource.getAt(row);
34934             var field = this.colModel.getDataIndex(col);
34935             var e = {
34936                 grid: this,
34937                 record: r,
34938                 field: field,
34939                 value: r.data[field],
34940                 row: row,
34941                 column: col,
34942                 cancel:false
34943             };
34944             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
34945                 this.editing = true;
34946                 var ed = this.colModel.getCellEditor(col, row);
34947                 
34948                 if (!ed) {
34949                     return;
34950                 }
34951                 if(!ed.rendered){
34952                     ed.render(ed.parentEl || document.body);
34953                 }
34954                 ed.field.reset();
34955                 (function(){ // complex but required for focus issues in safari, ie and opera
34956                     ed.row = row;
34957                     ed.col = col;
34958                     ed.record = r;
34959                     ed.on("complete", this.onEditComplete, this, {single: true});
34960                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
34961                     this.activeEditor = ed;
34962                     var v = r.data[field];
34963                     ed.startEdit(this.view.getCell(row, col), v);
34964                 }).defer(50, this);
34965             }
34966         }
34967     },
34968         
34969     /**
34970      * Stops any active editing
34971      */
34972     stopEditing : function(){
34973         if(this.activeEditor){
34974             this.activeEditor.completeEdit();
34975         }
34976         this.activeEditor = null;
34977     }
34978 });/*
34979  * Based on:
34980  * Ext JS Library 1.1.1
34981  * Copyright(c) 2006-2007, Ext JS, LLC.
34982  *
34983  * Originally Released Under LGPL - original licence link has changed is not relivant.
34984  *
34985  * Fork - LGPL
34986  * <script type="text/javascript">
34987  */
34988
34989 // private - not really -- you end up using it !
34990 // This is a support class used internally by the Grid components
34991
34992 /**
34993  * @class Roo.grid.GridEditor
34994  * @extends Roo.Editor
34995  * Class for creating and editable grid elements.
34996  * @param {Object} config any settings (must include field)
34997  */
34998 Roo.grid.GridEditor = function(field, config){
34999     if (!config && field.field) {
35000         config = field;
35001         field = Roo.factory(config.field, Roo.form);
35002     }
35003     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35004     field.monitorTab = false;
35005 };
35006
35007 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35008     
35009     /**
35010      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35011      */
35012     
35013     alignment: "tl-tl",
35014     autoSize: "width",
35015     hideEl : false,
35016     cls: "x-small-editor x-grid-editor",
35017     shim:false,
35018     shadow:"frame"
35019 });/*
35020  * Based on:
35021  * Ext JS Library 1.1.1
35022  * Copyright(c) 2006-2007, Ext JS, LLC.
35023  *
35024  * Originally Released Under LGPL - original licence link has changed is not relivant.
35025  *
35026  * Fork - LGPL
35027  * <script type="text/javascript">
35028  */
35029   
35030
35031   
35032 Roo.grid.PropertyRecord = Roo.data.Record.create([
35033     {name:'name',type:'string'},  'value'
35034 ]);
35035
35036
35037 Roo.grid.PropertyStore = function(grid, source){
35038     this.grid = grid;
35039     this.store = new Roo.data.Store({
35040         recordType : Roo.grid.PropertyRecord
35041     });
35042     this.store.on('update', this.onUpdate,  this);
35043     if(source){
35044         this.setSource(source);
35045     }
35046     Roo.grid.PropertyStore.superclass.constructor.call(this);
35047 };
35048
35049
35050
35051 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35052     setSource : function(o){
35053         this.source = o;
35054         this.store.removeAll();
35055         var data = [];
35056         for(var k in o){
35057             if(this.isEditableValue(o[k])){
35058                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35059             }
35060         }
35061         this.store.loadRecords({records: data}, {}, true);
35062     },
35063
35064     onUpdate : function(ds, record, type){
35065         if(type == Roo.data.Record.EDIT){
35066             var v = record.data['value'];
35067             var oldValue = record.modified['value'];
35068             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35069                 this.source[record.id] = v;
35070                 record.commit();
35071                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35072             }else{
35073                 record.reject();
35074             }
35075         }
35076     },
35077
35078     getProperty : function(row){
35079        return this.store.getAt(row);
35080     },
35081
35082     isEditableValue: function(val){
35083         if(val && val instanceof Date){
35084             return true;
35085         }else if(typeof val == 'object' || typeof val == 'function'){
35086             return false;
35087         }
35088         return true;
35089     },
35090
35091     setValue : function(prop, value){
35092         this.source[prop] = value;
35093         this.store.getById(prop).set('value', value);
35094     },
35095
35096     getSource : function(){
35097         return this.source;
35098     }
35099 });
35100
35101 Roo.grid.PropertyColumnModel = function(grid, store){
35102     this.grid = grid;
35103     var g = Roo.grid;
35104     g.PropertyColumnModel.superclass.constructor.call(this, [
35105         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
35106         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
35107     ]);
35108     this.store = store;
35109     this.bselect = Roo.DomHelper.append(document.body, {
35110         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
35111             {tag: 'option', value: 'true', html: 'true'},
35112             {tag: 'option', value: 'false', html: 'false'}
35113         ]
35114     });
35115     Roo.id(this.bselect);
35116     var f = Roo.form;
35117     this.editors = {
35118         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
35119         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
35120         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
35121         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
35122         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
35123     };
35124     this.renderCellDelegate = this.renderCell.createDelegate(this);
35125     this.renderPropDelegate = this.renderProp.createDelegate(this);
35126 };
35127
35128 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
35129     
35130     
35131     nameText : 'Name',
35132     valueText : 'Value',
35133     
35134     dateFormat : 'm/j/Y',
35135     
35136     
35137     renderDate : function(dateVal){
35138         return dateVal.dateFormat(this.dateFormat);
35139     },
35140
35141     renderBool : function(bVal){
35142         return bVal ? 'true' : 'false';
35143     },
35144
35145     isCellEditable : function(colIndex, rowIndex){
35146         return colIndex == 1;
35147     },
35148
35149     getRenderer : function(col){
35150         return col == 1 ?
35151             this.renderCellDelegate : this.renderPropDelegate;
35152     },
35153
35154     renderProp : function(v){
35155         return this.getPropertyName(v);
35156     },
35157
35158     renderCell : function(val){
35159         var rv = val;
35160         if(val instanceof Date){
35161             rv = this.renderDate(val);
35162         }else if(typeof val == 'boolean'){
35163             rv = this.renderBool(val);
35164         }
35165         return Roo.util.Format.htmlEncode(rv);
35166     },
35167
35168     getPropertyName : function(name){
35169         var pn = this.grid.propertyNames;
35170         return pn && pn[name] ? pn[name] : name;
35171     },
35172
35173     getCellEditor : function(colIndex, rowIndex){
35174         var p = this.store.getProperty(rowIndex);
35175         var n = p.data['name'], val = p.data['value'];
35176         
35177         if(typeof(this.grid.customEditors[n]) == 'string'){
35178             return this.editors[this.grid.customEditors[n]];
35179         }
35180         if(typeof(this.grid.customEditors[n]) != 'undefined'){
35181             return this.grid.customEditors[n];
35182         }
35183         if(val instanceof Date){
35184             return this.editors['date'];
35185         }else if(typeof val == 'number'){
35186             return this.editors['number'];
35187         }else if(typeof val == 'boolean'){
35188             return this.editors['boolean'];
35189         }else{
35190             return this.editors['string'];
35191         }
35192     }
35193 });
35194
35195 /**
35196  * @class Roo.grid.PropertyGrid
35197  * @extends Roo.grid.EditorGrid
35198  * This class represents the  interface of a component based property grid control.
35199  * <br><br>Usage:<pre><code>
35200  var grid = new Roo.grid.PropertyGrid("my-container-id", {
35201       
35202  });
35203  // set any options
35204  grid.render();
35205  * </code></pre>
35206   
35207  * @constructor
35208  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35209  * The container MUST have some type of size defined for the grid to fill. The container will be
35210  * automatically set to position relative if it isn't already.
35211  * @param {Object} config A config object that sets properties on this grid.
35212  */
35213 Roo.grid.PropertyGrid = function(container, config){
35214     config = config || {};
35215     var store = new Roo.grid.PropertyStore(this);
35216     this.store = store;
35217     var cm = new Roo.grid.PropertyColumnModel(this, store);
35218     store.store.sort('name', 'ASC');
35219     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
35220         ds: store.store,
35221         cm: cm,
35222         enableColLock:false,
35223         enableColumnMove:false,
35224         stripeRows:false,
35225         trackMouseOver: false,
35226         clicksToEdit:1
35227     }, config));
35228     this.getGridEl().addClass('x-props-grid');
35229     this.lastEditRow = null;
35230     this.on('columnresize', this.onColumnResize, this);
35231     this.addEvents({
35232          /**
35233              * @event beforepropertychange
35234              * Fires before a property changes (return false to stop?)
35235              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
35236              * @param {String} id Record Id
35237              * @param {String} newval New Value
35238          * @param {String} oldval Old Value
35239              */
35240         "beforepropertychange": true,
35241         /**
35242              * @event propertychange
35243              * Fires after a property changes
35244              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
35245              * @param {String} id Record Id
35246              * @param {String} newval New Value
35247          * @param {String} oldval Old Value
35248              */
35249         "propertychange": true
35250     });
35251     this.customEditors = this.customEditors || {};
35252 };
35253 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
35254     
35255      /**
35256      * @cfg {Object} customEditors map of colnames=> custom editors.
35257      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
35258      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
35259      * false disables editing of the field.
35260          */
35261     
35262       /**
35263      * @cfg {Object} propertyNames map of property Names to their displayed value
35264          */
35265     
35266     render : function(){
35267         Roo.grid.PropertyGrid.superclass.render.call(this);
35268         this.autoSize.defer(100, this);
35269     },
35270
35271     autoSize : function(){
35272         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
35273         if(this.view){
35274             this.view.fitColumns();
35275         }
35276     },
35277
35278     onColumnResize : function(){
35279         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
35280         this.autoSize();
35281     },
35282     /**
35283      * Sets the data for the Grid
35284      * accepts a Key => Value object of all the elements avaiable.
35285      * @param {Object} data  to appear in grid.
35286      */
35287     setSource : function(source){
35288         this.store.setSource(source);
35289         //this.autoSize();
35290     },
35291     /**
35292      * Gets all the data from the grid.
35293      * @return {Object} data  data stored in grid
35294      */
35295     getSource : function(){
35296         return this.store.getSource();
35297     }
35298 });/*
35299  * Based on:
35300  * Ext JS Library 1.1.1
35301  * Copyright(c) 2006-2007, Ext JS, LLC.
35302  *
35303  * Originally Released Under LGPL - original licence link has changed is not relivant.
35304  *
35305  * Fork - LGPL
35306  * <script type="text/javascript">
35307  */
35308  
35309 /**
35310  * @class Roo.LoadMask
35311  * A simple utility class for generically masking elements while loading data.  If the element being masked has
35312  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
35313  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
35314  * element's UpdateManager load indicator and will be destroyed after the initial load.
35315  * @constructor
35316  * Create a new LoadMask
35317  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
35318  * @param {Object} config The config object
35319  */
35320 Roo.LoadMask = function(el, config){
35321     this.el = Roo.get(el);
35322     Roo.apply(this, config);
35323     if(this.store){
35324         this.store.on('beforeload', this.onBeforeLoad, this);
35325         this.store.on('load', this.onLoad, this);
35326         this.store.on('loadexception', this.onLoad, this);
35327         this.removeMask = false;
35328     }else{
35329         var um = this.el.getUpdateManager();
35330         um.showLoadIndicator = false; // disable the default indicator
35331         um.on('beforeupdate', this.onBeforeLoad, this);
35332         um.on('update', this.onLoad, this);
35333         um.on('failure', this.onLoad, this);
35334         this.removeMask = true;
35335     }
35336 };
35337
35338 Roo.LoadMask.prototype = {
35339     /**
35340      * @cfg {Boolean} removeMask
35341      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
35342      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
35343      */
35344     /**
35345      * @cfg {String} msg
35346      * The text to display in a centered loading message box (defaults to 'Loading...')
35347      */
35348     msg : 'Loading...',
35349     /**
35350      * @cfg {String} msgCls
35351      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
35352      */
35353     msgCls : 'x-mask-loading',
35354
35355     /**
35356      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
35357      * @type Boolean
35358      */
35359     disabled: false,
35360
35361     /**
35362      * Disables the mask to prevent it from being displayed
35363      */
35364     disable : function(){
35365        this.disabled = true;
35366     },
35367
35368     /**
35369      * Enables the mask so that it can be displayed
35370      */
35371     enable : function(){
35372         this.disabled = false;
35373     },
35374
35375     // private
35376     onLoad : function(){
35377         this.el.unmask(this.removeMask);
35378     },
35379
35380     // private
35381     onBeforeLoad : function(){
35382         if(!this.disabled){
35383             this.el.mask(this.msg, this.msgCls);
35384         }
35385     },
35386
35387     // private
35388     destroy : function(){
35389         if(this.store){
35390             this.store.un('beforeload', this.onBeforeLoad, this);
35391             this.store.un('load', this.onLoad, this);
35392             this.store.un('loadexception', this.onLoad, this);
35393         }else{
35394             var um = this.el.getUpdateManager();
35395             um.un('beforeupdate', this.onBeforeLoad, this);
35396             um.un('update', this.onLoad, this);
35397             um.un('failure', this.onLoad, this);
35398         }
35399     }
35400 };/*
35401  * Based on:
35402  * Ext JS Library 1.1.1
35403  * Copyright(c) 2006-2007, Ext JS, LLC.
35404  *
35405  * Originally Released Under LGPL - original licence link has changed is not relivant.
35406  *
35407  * Fork - LGPL
35408  * <script type="text/javascript">
35409  */
35410 Roo.XTemplate = function(){
35411     Roo.XTemplate.superclass.constructor.apply(this, arguments);
35412     var s = this.html;
35413
35414     s = ['<tpl>', s, '</tpl>'].join('');
35415
35416     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
35417
35418     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
35419     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
35420     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
35421     var m, id = 0;
35422     var tpls = [];
35423
35424     while(m = s.match(re)){
35425        var m2 = m[0].match(nameRe);
35426        var m3 = m[0].match(ifRe);
35427        var m4 = m[0].match(execRe);
35428        var exp = null, fn = null, exec = null;
35429        var name = m2 && m2[1] ? m2[1] : '';
35430        if(m3){
35431            exp = m3 && m3[1] ? m3[1] : null;
35432            if(exp){
35433                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
35434            }
35435        }
35436        if(m4){
35437            exp = m4 && m4[1] ? m4[1] : null;
35438            if(exp){
35439                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
35440            }
35441        }
35442        if(name){
35443            switch(name){
35444                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
35445                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
35446                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
35447            }
35448        }
35449        tpls.push({
35450             id: id,
35451             target: name,
35452             exec: exec,
35453             test: fn,
35454             body: m[1]||''
35455         });
35456        s = s.replace(m[0], '{xtpl'+ id + '}');
35457        ++id;
35458     }
35459     for(var i = tpls.length-1; i >= 0; --i){
35460         this.compileTpl(tpls[i]);
35461     }
35462     this.master = tpls[tpls.length-1];
35463     this.tpls = tpls;
35464 };
35465 Roo.extend(Roo.XTemplate, Roo.Template, {
35466
35467     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
35468
35469     applySubTemplate : function(id, values, parent){
35470         var t = this.tpls[id];
35471         if(t.test && !t.test.call(this, values, parent)){
35472             return '';
35473         }
35474         if(t.exec && t.exec.call(this, values, parent)){
35475             return '';
35476         }
35477         var vs = t.target ? t.target.call(this, values, parent) : values;
35478         parent = t.target ? values : parent;
35479         if(t.target && vs instanceof Array){
35480             var buf = [];
35481             for(var i = 0, len = vs.length; i < len; i++){
35482                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
35483             }
35484             return buf.join('');
35485         }
35486         return t.compiled.call(this, vs, parent);
35487     },
35488
35489     compileTpl : function(tpl){
35490         var fm = Roo.util.Format;
35491         var useF = this.disableFormats !== true;
35492         var sep = Roo.isGecko ? "+" : ",";
35493         var fn = function(m, name, format, args){
35494             if(name.substr(0, 4) == 'xtpl'){
35495                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
35496             }
35497             var v;
35498             if(name.indexOf('.') != -1){
35499                 v = name;
35500             }else{
35501                 v = "values['" + name + "']";
35502             }
35503             if(format && useF){
35504                 args = args ? ',' + args : "";
35505                 if(format.substr(0, 5) != "this."){
35506                     format = "fm." + format + '(';
35507                 }else{
35508                     format = 'this.call("'+ format.substr(5) + '", ';
35509                     args = ", values";
35510                 }
35511             }else{
35512                 args= ''; format = "("+v+" === undefined ? '' : ";
35513             }
35514             return "'"+ sep + format + v + args + ")"+sep+"'";
35515         };
35516         var body;
35517         // branched to use + in gecko and [].join() in others
35518         if(Roo.isGecko){
35519             body = "tpl.compiled = function(values, parent){ return '" +
35520                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
35521                     "';};";
35522         }else{
35523             body = ["tpl.compiled = function(values, parent){ return ['"];
35524             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
35525             body.push("'].join('');};");
35526             body = body.join('');
35527         }
35528         /** eval:var:zzzzzzz */
35529         eval(body);
35530         return this;
35531     },
35532
35533     applyTemplate : function(values){
35534         return this.master.compiled.call(this, values, {});
35535         var s = this.subs;
35536     },
35537
35538     apply : function(){
35539         return this.applyTemplate.apply(this, arguments);
35540     },
35541
35542     compile : function(){return this;}
35543 });
35544
35545 Roo.XTemplate.from = function(el){
35546     el = Roo.getDom(el);
35547     return new Roo.XTemplate(el.value || el.innerHTML);
35548 };/*
35549  * Original code for Roojs - LGPL
35550  * <script type="text/javascript">
35551  */
35552  
35553 /**
35554  * @class Roo.XComponent
35555  * A delayed Element creator...
35556  * 
35557  * Mypart.xyx = new Roo.XComponent({
35558
35559     parent : 'Mypart.xyz', // empty == document.element.!!
35560     order : '001',
35561     name : 'xxxx'
35562     region : 'xxxx'
35563     disabled : function() {} 
35564      
35565     tree : function() { // return an tree of xtype declared components
35566         var MODULE = this;
35567         return 
35568         {
35569             xtype : 'NestedLayoutPanel',
35570             // technicall
35571         }
35572      ]
35573  *})
35574  * @extends Roo.util.Observable
35575  * @constructor
35576  * @param cfg {Object} configuration of component
35577  * 
35578  */
35579 Roo.XComponent = function(cfg) {
35580     Roo.apply(this, cfg);
35581     this.addEvents({ 
35582         /**
35583              * @event built
35584              * Fires when this the componnt is built
35585              * @param {Roo.XComponent} c the component
35586              */
35587         'built' : true,
35588         /**
35589              * @event buildcomplete
35590              * Fires on the top level element when all elements have been built
35591              * @param {Roo.XComponent} c the top level component.
35592          */
35593         'buildcomplete' : true,
35594         
35595     });
35596     
35597     Roo.XComponent.register(this);
35598     this.modules = false;
35599     this.el = false; // where the layout goes..
35600     
35601     
35602 }
35603 Roo.extend(Roo.XComponent, Roo.util.Observable, {
35604     /**
35605      * @property el
35606      * The created element (with Roo.factory())
35607      * @type {Roo.Layout}
35608      */
35609     el  : false,
35610     
35611     /**
35612      * @property el
35613      * for BC  - use el in new code
35614      * @type {Roo.Layout}
35615      */
35616     panel : false,
35617     
35618     /**
35619      * @property layout
35620      * for BC  - use el in new code
35621      * @type {Roo.Layout}
35622      */
35623     layout : false,
35624     
35625      /**
35626      * @cfg {Function|boolean} disabled
35627      * If this module is disabled by some rule, return true from the funtion
35628      */
35629     disabled : false,
35630     
35631     /**
35632      * @cfg {String} parent 
35633      * Name of parent element which it get xtype added to..
35634      */
35635     parent: false,
35636     
35637     /**
35638      * @cfg {String} order
35639      * Used to set the order in which elements are created (usefull for multiple tabs)
35640      */
35641     
35642     order : false,
35643     /**
35644      * @cfg {String} name
35645      * String to display while loading.
35646      */
35647     name : false,
35648     /**
35649      * @cfg {Array} items
35650      * A single item array - the first element is the root of the tree..
35651      * It's done this way to stay compatible with the Xtype system...
35652      */
35653     items : false,
35654      
35655      
35656     
35657 });
35658
35659 Roo.apply(Roo.XComponent, {
35660     
35661     /**
35662      * @property  buildCompleted
35663      * True when the builder has completed building the interface.
35664      * @type Boolean
35665      */
35666     buildCompleted : false,
35667      
35668     /**
35669      * @property  topModule
35670      * the upper most module - uses document.element as it's constructor.
35671      * @type Object
35672      */
35673      
35674     topModule  : false,
35675       
35676     /**
35677      * @property  modules
35678      * array of modules to be created by registration system.
35679      * @type Roo.XComponent
35680      */
35681     
35682     modules : [],
35683       
35684     
35685     /**
35686      * Register components to be built later.
35687      *
35688      * This solves the following issues
35689      * - Building is not done on page load, but after an authentication process has occured.
35690      * - Interface elements are registered on page load
35691      * - Parent Interface elements may not be loaded before child, so this handles that..
35692      * 
35693      *
35694      * example:
35695      * 
35696      * MyApp.register({
35697           order : '000001',
35698           module : 'Pman.Tab.projectMgr',
35699           region : 'center',
35700           parent : 'Pman.layout',
35701           disabled : false,  // or use a function..
35702         })
35703      
35704      * * @param {Object} details about module
35705      */
35706     register : function(obj) {
35707         this.modules.push(obj);
35708          
35709     },
35710     /**
35711      * convert a string to an object..
35712      * 
35713      */
35714     
35715     toObject : function(str)
35716     {
35717         if (!str || typeof(str) == 'object') {
35718             return str;
35719         }
35720         var ar = str.split('.');
35721         var rt, o;
35722         rt = ar.shift();
35723             /** eval:var:o */
35724         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
35725         if (o === false) {
35726             throw "Module not found : " + str;
35727         }
35728         Roo.each(ar, function(e) {
35729             if (typeof(o[e]) == 'undefined') {
35730                 throw "Module not found : " + str;
35731             }
35732             o = o[e];
35733         });
35734         return o;
35735         
35736     },
35737     
35738     
35739     /**
35740      * move modules into their correct place in the tree..
35741      * 
35742      */
35743     preBuild : function ()
35744     {
35745         
35746         Roo.each(this.modules , function (obj)
35747         {
35748             obj.parent = this.toObject(obj.parent);
35749             
35750             if (!obj.parent) {
35751                 this.topModule = obj;
35752                 return;
35753             }
35754             
35755             if (!obj.parent.modules) {
35756                 obj.parent.modules = new Roo.util.MixedCollection(false, 
35757                     function(o) { return o.order + '' }
35758                 );
35759             }
35760             
35761             obj.parent.modules.add(obj);
35762         }, this);
35763     },
35764     
35765      /**
35766      * make a list of modules to build.
35767      * @return {Array} list of modules. 
35768      */ 
35769     
35770     buildOrder : function()
35771     {
35772         var _this = this;
35773         var cmp = function(a,b) {   
35774             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
35775         };
35776         
35777         if (!this.topModule || !this.topModule.modules) {
35778             throw "No top level modules to build";
35779         }
35780        
35781         // make a flat list in order of modules to build.
35782         var mods = [ this.topModule ];
35783         
35784         
35785         // add modules to their parents..
35786         var addMod = function(m) {
35787            // console.log(m.modKey);
35788             
35789             mods.push(m);
35790             if (m.modules) {
35791                 m.modules.keySort('ASC',  cmp );
35792                 m.modules.each(addMod);
35793             }
35794             // not sure if this is used any more..
35795             if (m.finalize) {
35796                 m.finalize.name = m.name + " (clean up) ";
35797                 mods.push(m.finalize);
35798             }
35799             
35800         }
35801         this.topModule.modules.keySort('ASC',  cmp );
35802         this.topModule.modules.each(addMod);
35803         return mods;
35804     },
35805     
35806      /**
35807      * Build the registered modules.
35808      * @param {Object} parent element.
35809      * @param {Function} optional method to call after module has been added.
35810      * 
35811      */ 
35812    
35813     build : function() 
35814     {
35815         
35816         this.preBuild();
35817         var mods = this.buildOrder();
35818       
35819         //this.allmods = mods;
35820         //console.log(mods);
35821         //return;
35822         if (!mods.length) { // should not happen
35823             throw "NO modules!!!";
35824         }
35825         
35826         
35827         
35828         // flash it up as modal - so we store the mask!?
35829         Roo.MessageBox.show({ title: 'loading' });
35830         Roo.MessageBox.show({
35831            title: "Please wait...",
35832            msg: "Building Interface...",
35833            width:450,
35834            progress:true,
35835            closable:false,
35836            modal: false
35837           
35838         });
35839         var total = mods.length;
35840         
35841         var _this = this;
35842         var progressRun = function() {
35843             if (!mods.length) {
35844                 console.log('hide?');
35845                 Roo.MessageBox.hide();
35846                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
35847                 return;    
35848             }
35849             
35850             var m = mods.shift();
35851             console.log(m);
35852             if (typeof(m) == 'function') { // not sure if this is supported any more..
35853                 m.call(this);
35854                 return progressRun.defer(10, _this);
35855             } 
35856             
35857             Roo.MessageBox.updateProgress(
35858                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
35859                     " of " + total + 
35860                     (m.name ? (' - ' + m.name) : '')
35861                     );
35862             
35863          
35864             
35865             var disabled = (typeof(m.disabled) == 'function') ?
35866                 m.disabled.call(m.module.disabled) : m.disabled;    
35867             
35868             
35869             if (disabled) {
35870                 return progressRun(); // we do not update the display!
35871             }
35872             
35873             if (!m.parent) {
35874                 // it's a top level one..
35875                 var layoutbase = new Ext.BorderLayout(document.body, {
35876                
35877                     center: {
35878                          titlebar: false,
35879                          autoScroll:false,
35880                          closeOnTab: true,
35881                          tabPosition: 'top',
35882                          //resizeTabs: true,
35883                          alwaysShowTabs: true,
35884                          minTabWidth: 140
35885                     }
35886                 });
35887                 var tree = m.tree();
35888                 tree.region = 'center';
35889                 m.el = layoutbase.addxtype(tree);
35890                 m.panel = m.el;
35891                 m.layout = m.panel.layout;    
35892                 return progressRun.defer(10, _this);
35893             }
35894             
35895             var tree = m.tree();
35896             tree.region = tree.region || m.region;
35897             m.el = m.parent.el.addxtype(tree);
35898             m.fireEvent('built', m);
35899             m.panel = m.el;
35900             m.layout = m.panel.layout;    
35901             progressRun.defer(10, _this); 
35902             
35903         }
35904         progressRun.defer(1, _this);
35905      
35906         
35907         
35908     }
35909      
35910    
35911     
35912     
35913 });
35914  //<script type="text/javascript">
35915
35916
35917 /**
35918  * @class Roo.Login
35919  * @extends Roo.LayoutDialog
35920  * A generic Login Dialog..... - only one needed in theory!?!?
35921  *
35922  * Fires XComponent builder on success...
35923  * 
35924  * Sends 
35925  *    username,password, lang = for login actions.
35926  *    check = 1 for periodic checking that sesion is valid.
35927  *    passwordRequest = email request password
35928  *    logout = 1 = to logout
35929  * 
35930  * Affects: (this id="????" elements)
35931  *   loading  (removed) (used to indicate application is loading)
35932  *   loading-mask (hides) (used to hide application when it's building loading)
35933  *   
35934  * 
35935  * Usage: 
35936  *    
35937  * 
35938  * Myapp.login = Roo.Login({
35939      url: xxxx,
35940    
35941      realm : 'Myapp', 
35942      
35943      
35944      method : 'POST',
35945      
35946      
35947      * 
35948  })
35949  * 
35950  * 
35951  * 
35952  **/
35953  
35954 Roo.Login = function(cfg)
35955 {
35956     this.addEvents({
35957         'refreshed' : true,
35958     });
35959     
35960     Roo.apply(this,cfg);
35961     
35962     Roo.onReady(function() {
35963         this.onLoad();
35964     }, this);
35965     // call parent..
35966     
35967    
35968     Roo.Login.superclass.constructor.call(this, this);
35969     //this.addxtype(this.items[0]);
35970     
35971     
35972 }
35973
35974
35975 Roo.extend(Roo.Login, Roo.LayoutDialog, {
35976     
35977     /**
35978      * @cfg {String} method
35979      * Method used to query for login details.
35980      */
35981     
35982     method : 'POST',
35983     /**
35984      * @cfg {String} url
35985      * URL to query login data. - eg. baseURL + '/Login.php'
35986      */
35987     url : '',
35988     
35989     /**
35990      * @property user
35991      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
35992      * @type {Object} 
35993      */
35994     user : false,
35995     /**
35996      * @property checkFails
35997      * Number of times we have attempted to get authentication check, and failed.
35998      * @type {Number} 
35999      */
36000     checkFails : 0,
36001       /**
36002      * @property intervalID
36003      * The window interval that does the constant login checking.
36004      * @type {Number} 
36005      */
36006     intervalID : 0,
36007     
36008     
36009     onLoad : function() // called on page load...
36010     {
36011         // load 
36012          
36013         if (Roo.get('loading')) { // clear any loading indicator..
36014             Roo.get('loading').remove();
36015         }
36016         
36017         //this.switchLang('en'); // set the language to english..
36018        
36019         this.check({
36020             success:  function(response, opts)  {  // check successfull...
36021             
36022                 var res = this.processResponse(response);
36023                 this.checkFails =0;
36024                 if (!res.success) { // error!
36025                     this.checkFails = 5;
36026                     //console.log('call failure');
36027                     return this.failure(response,opts);
36028                 }
36029                 
36030                 if (!res.data.id) { // id=0 == login failure.
36031                     return this.show();
36032                 }
36033                 
36034                               
36035                         //console.log(success);
36036                 this.fillAuth(res.data);   
36037                 this.checkFails =0;
36038                 Roo.XComponent.build();
36039             },
36040             failure : this.show
36041         });
36042         
36043     }, 
36044     
36045     
36046     check: function(cfg) // called every so often to refresh cookie etc..
36047     {
36048         if (cfg.again) { // could be undefined..
36049             this.checkFails++;
36050         } else {
36051             this.checkFails = 0;
36052         }
36053         var _this = this;
36054         if (this.sending) {
36055             if ( this.checkFails > 4) {
36056                 Roo.MessageBox.alert("Error",  
36057                     "Error getting authentication status. - try reloading, or wait a while", function() {
36058                         _this.sending = false;
36059                     }); 
36060                 return;
36061             }
36062             cfg.again = true;
36063             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36064             return;
36065         }
36066         this.sending = true;
36067         
36068         Roo.Ajax.request({  
36069             url: this.url,
36070             params: {
36071                 getAuthUser: true
36072             },  
36073             method: this.method,
36074             success:  cfg.success || this.success,
36075             failure : cfg.failure || this.failure,
36076             scope : this,
36077             callCfg : cfg
36078               
36079         });  
36080     }, 
36081     
36082     
36083     logout: function()
36084     {
36085         window.onbeforeunload = function() { }; // false does not work for IE..
36086         this.user = false;
36087         var _this = this;
36088         
36089         Roo.Ajax.request({  
36090             url: this.url,
36091             params: {
36092                 logout: 1
36093             },  
36094             method: 'GET',
36095             failure : function() {
36096                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36097                     document.location = document.location.toString() + '?ts=' + Math.random();
36098                 });
36099                 
36100             },
36101             success : function() {
36102                 _this.user = false;
36103                 this.checkFails =0;
36104                 // fixme..
36105                 document.location = document.location.toString() + '?ts=' + Math.random();
36106             }
36107               
36108               
36109         }); 
36110     },
36111     
36112     processResponse : function (response)
36113     {
36114         var res = '';
36115         try {
36116             res = Roo.decode(response.responseText);
36117             // oops...
36118             if (typeof(res) != 'object') {
36119                 res = { success : false, errorMsg : res, errors : true };
36120             }
36121             if (typeof(res.success) == 'undefined') {
36122                 res.success = false;
36123             }
36124             
36125         } catch(e) {
36126             res = { success : false,  errorMsg : response.responseText, errors : true };
36127         }
36128         return res;
36129     },
36130     
36131     success : function(response, opts)  // check successfull...
36132     {  
36133         this.sending = false;
36134         var res = this.processResponse(response);
36135         if (!res.success) {
36136             return this.failure(response, opts);
36137         }
36138         if (!res.data || !res.data.id) {
36139             return this.failure(response,opts);
36140         }
36141         //console.log(res);
36142         this.fillAuth(res.data);
36143         
36144         this.checkFails =0;
36145         
36146     },
36147     
36148     
36149     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
36150     {
36151         this.authUser = -1;
36152         this.sending = false;
36153         var res = this.processResponse(response);
36154         //console.log(res);
36155         if ( this.checkFails > 2) {
36156         
36157             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
36158                 "Error getting authentication status. - try reloading"); 
36159             return;
36160         }
36161         opts.callCfg.again = true;
36162         this.check.defer(1000, this, [ opts.callCfg ]);
36163         return;  
36164     },
36165     
36166     
36167     
36168     fillAuth: function(au) {
36169         this.startAuthCheck();
36170         this.authUserId = au.id;
36171         this.authUser = au;
36172         this.lastChecked = new Date();
36173         this.fireEvent('refreshed', au);
36174         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
36175         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
36176         au.lang = au.lang || 'en';
36177         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
36178         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
36179         this.switchLang(au.lang );
36180         
36181      
36182         // open system... - -on setyp..
36183         if (this.authUserId  < 0) {
36184             Roo.MessageBox.alert("Warning", 
36185                 "This is an open system - please set up a admin user with a password.");  
36186         }
36187          
36188         //Pman.onload(); // which should do nothing if it's a re-auth result...
36189         
36190              
36191     },
36192     
36193     startAuthCheck : function() // starter for timeout checking..
36194     {
36195         if (this.intervalID) { // timer already in place...
36196             return false;
36197         }
36198         var _this = this;
36199         this.intervalID =  window.setInterval(function() {
36200               _this.check(false);
36201             }, 120000); // every 120 secs = 2mins..
36202         
36203         
36204     },
36205          
36206     
36207     switchLang : function (lang) 
36208     {
36209         _T = typeof(_T) == 'undefined' ? false : _T;
36210           if (!_T || !lang.length) {
36211             return;
36212         }
36213         
36214         if (!_T && lang != 'en') {
36215             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36216             return;
36217         }
36218         
36219         if (typeof(_T.en) == 'undefined') {
36220             _T.en = {};
36221             Roo.apply(_T.en, _T);
36222         }
36223         
36224         if (typeof(_T[lang]) == 'undefined') {
36225             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36226             return;
36227         }
36228         
36229         
36230         Roo.apply(_T, _T[lang]);
36231         // just need to set the text values for everything...
36232         var _this = this;
36233         /* this will not work ...
36234         if (this.form) { 
36235             
36236                
36237             function formLabel(name, val) {
36238                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
36239             }
36240             
36241             formLabel('password', "Password"+':');
36242             formLabel('username', "Email Address"+':');
36243             formLabel('lang', "Language"+':');
36244             this.dialog.setTitle("Login");
36245             this.dialog.buttons[0].setText("Forgot Password");
36246             this.dialog.buttons[1].setText("Login");
36247         }
36248         */
36249         
36250         
36251     },
36252     
36253     
36254     title: "Login",
36255     modal: true,
36256     width:  350,
36257     //height: 230,
36258     height: 180,
36259     shadow: true,
36260     minWidth:200,
36261     minHeight:180,
36262     //proxyDrag: true,
36263     closable: false,
36264     draggable: false,
36265     collapsible: false,
36266     resizable: false,
36267     center: {  // needed??
36268         autoScroll:false,
36269         titlebar: false,
36270        // tabPosition: 'top',
36271         hideTabs: true,
36272         closeOnTab: true,
36273         alwaysShowTabs: false
36274     } ,
36275     listeners : {
36276         
36277         show  : function(dlg)
36278         {
36279             //console.log(this);
36280             this.form = this.layout.getRegion('center').activePanel.form;
36281             this.form.dialog = dlg;
36282             this.buttons[0].form = this.form;
36283             this.buttons[0].dialog = dlg
36284             this.buttons[1].form = this.form;
36285             this.buttons[1].dialog = dlg;
36286            
36287            //this.resizeToLogo.defer(1000,this);
36288             // this is all related to resizing for logos..
36289             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
36290            //// if (!sz) {
36291              //   this.resizeToLogo.defer(1000,this);
36292              //   return;
36293            // }
36294             //var w = Ext.lib.Dom.getViewWidth() - 100;
36295             //var h = Ext.lib.Dom.getViewHeight() - 100;
36296             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
36297             //this.center();
36298             if (this.disabled) {
36299                 this.hide();
36300                 return;
36301             }
36302             
36303             if (this.user.id < 0) { // used for inital setup situations.
36304                 return;
36305             }
36306             
36307             if (this.intervalID) {
36308                 // remove the timer
36309                 window.clearInterval(this.intervalID);
36310                 this.intervalID = false;
36311             }
36312             
36313             
36314             if (Roo.get('loading')) {
36315                 Roo.get('loading').remove();
36316             }
36317             if (Roo.get('loading-mask')) {
36318                 Roo.get('loading-mask').hide();
36319             }
36320             
36321             //incomming._node = tnode;
36322             this.form.reset();
36323             //this.dialog.modal = !modal;
36324             //this.dialog.show();
36325             this.el.unmask(); 
36326             
36327             
36328             this.form.setValues({
36329                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
36330                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
36331             });
36332             
36333             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
36334             if (this.form.findField('username').getValue().length > 0 ){
36335                 this.form.findField('password').focus();
36336             } else {
36337                this.form.findField('username').focus();
36338             }
36339     
36340         }
36341     },
36342     items : [
36343          {
36344        
36345             xtype : 'ContentPanel',
36346             xns : Roo,
36347             region: 'center',
36348             fitToFrame : true,
36349             
36350             items : [
36351     
36352                 {
36353                
36354                     xtype : 'Form',
36355                     xns : Roo.form,
36356                     labelWidth: 100,
36357                     style : 'margin: 10px;',
36358                     
36359                     listeners : {
36360                         actionfailed : function(f, act) {
36361                             // form can return { errors: .... }
36362                                 
36363                             //act.result.errors // invalid form element list...
36364                             //act.result.errorMsg// invalid form element list...
36365                             
36366                             this.dialog.el.unmask();
36367                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
36368                                         "Login failed - communication error - try again.");
36369                                       
36370                         },
36371                         actioncomplete: function(re, act) {
36372                              
36373                             Roo.state.Manager.set(
36374                                 this.dialog.realm + '.username',  
36375                                     this.findField('username').getValue()
36376                             );
36377                             Roo.state.Manager.set(
36378                                 this.dialog.realm + '.lang',  
36379                                 this.findField('lang').getValue() 
36380                             );
36381                             
36382                             this.dialog.fillAuth(act.result.data);
36383                               
36384                             this.dialog.hide();
36385                             
36386                             if (Roo.get('loading-mask')) {
36387                                 Roo.get('loading-mask').show();
36388                             }
36389                             Roo.XComponent.build();
36390                             
36391                              
36392                             
36393                         }
36394                     },
36395                     items : [
36396                         {
36397                             xtype : 'TextField',
36398                             xns : Roo.form,
36399                             fieldLabel: "Email Address",
36400                             name: 'username',
36401                             width:200,
36402                             autoCreate : {tag: "input", type: "text", size: "20"}
36403                         },
36404                         {
36405                             xtype : 'TextField',
36406                             xns : Roo.form,
36407                             fieldLabel: "Password",
36408                             inputType: 'password',
36409                             name: 'password',
36410                             width:200,
36411                             autoCreate : {tag: "input", type: "text", size: "20"},
36412                             listeners : {
36413                                 specialkey : function(e,ev) {
36414                                     if (ev.keyCode == 13) {
36415                                         this.form.dialog.el.mask("Logging in");
36416                                         this.form.doAction('submit', {
36417                                             url: this.form.dialog.url,
36418                                             method: this.form.dialog.method,
36419                                         });
36420                                     }
36421                                 }
36422                             }  
36423                         },
36424                         {
36425                             xtype : 'ComboBox',
36426                             xns : Roo.form,
36427                             fieldLabel: "Language",
36428                             name : 'langdisp',
36429                             store: {
36430                                 xtype : 'SimpleStore',
36431                                 fields: ['lang', 'ldisp'],
36432                                 data : [
36433                                     [ 'en', 'English' ],
36434                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
36435                                     [ 'zh_CN', '\u7C21\u4E2D' ]
36436                                 ]
36437                             },
36438                             
36439                             valueField : 'lang',
36440                             hiddenName:  'lang',
36441                             width: 200,
36442                             displayField:'ldisp',
36443                             typeAhead: false,
36444                             editable: false,
36445                             mode: 'local',
36446                             triggerAction: 'all',
36447                             emptyText:'Select a Language...',
36448                             selectOnFocus:true,
36449                             listeners : {
36450                                 select :  function(cb, rec, ix) {
36451                                     this.form.switchLang(rec.data.lang);
36452                                 }
36453                             }
36454                         
36455                         }
36456                     ]
36457                 }
36458                   
36459                 
36460             ]
36461         }
36462     ],
36463     buttons : [
36464         {
36465             xtype : 'Button',
36466             xns : 'Roo',
36467             text : "Forgot Password",
36468             listeners : {
36469                 click : function() {
36470                     //console.log(this);
36471                     var n = this.form.findField('username').getValue();
36472                     if (!n.length) {
36473                         Roo.MessageBox.alert("Error", "Fill in your email address");
36474                         return;
36475                     }
36476                     Roo.Ajax.request({
36477                         url: this.dialog.url,
36478                         params: {
36479                             passwordRequest: n
36480                         },
36481                         method: this.dialog.method,
36482                         success:  function(response, opts)  {  // check successfull...
36483                         
36484                             var res = this.dialog.processResponse(response);
36485                             if (!res.success) { // error!
36486                                Roo.MessageBox.alert("Error" ,
36487                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
36488                                return;
36489                             }
36490                             Roo.MessageBox.alert("Notice" ,
36491                                 "Please check you email for the Password Reset message");
36492                         },
36493                         failure : function() {
36494                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
36495                         }
36496                         
36497                     });
36498                 }
36499             }
36500         },
36501         {
36502             xtype : 'Button',
36503             xns : 'Roo',
36504             text : "Login",
36505             listeners : {
36506                 
36507                 click : function () {
36508                         
36509                     this.dialog.el.mask("Logging in");
36510                     this.form.doAction('submit', {
36511                             url: this.dialog.url,
36512                             method: this.dialog.method
36513                     });
36514                 }
36515             }
36516         }
36517     ]
36518   
36519   
36520 })
36521  
36522
36523
36524